javascript

Creating a dynamic map in the edge

A few months ago, Wes Bos tweeted "Really neat feature of the new SvelteKit website, this dynamic SVG is rendered on the edge and changes based on your location". I decided to port the code to Next.js:

World globe

Unless you are using a VPN, the image above should pin where you are. Let's check some details (or check this pull request):

Middleware

Middlewares ↗︎ were introduced in Next.js 12 and here a middleware is being used to identify where the user comes from. Middlewares can be also helpful for A/B testing, internationalization, bot protection and more.

middleware.ts
export async function middleware(req: NextRequest) {
  const { nextUrl: url, geo } = req;
  const country = geo?.country || 'US';
  // ...more geo data
 
  url.searchParams.set('country', country);
 
  return NextResponse.rewrite(url);
}

Dependencies

The map is plotted with the help of the d3-geo, d3-geo-projection and topojson-client. The d3-geo might take you back to the geography classes. Here is a different map with the same location info:

World map

Endpoint returning an SVG

The endpoint to return an SVG is similar to the Vue implementation, however since this is Next.js I am using getServerSideProps to get the location information and rendering the SVG with a handler function. The endpoint file lives in the api folder. I chose this folder instead of the regular pages folder since Next.js expects React components instead of content with a different mime-type (context). A short draft:

api/world-globe/index.ts
function render({ city, country, latitude, longitude }) {
  // ...return a <svg>
}
 
export const getServerSideProps: GetServerSideProps = async ({ query }) => {
  return {
    props: query,
  };
};
 
export default function handler(req: NextApiRequest, res: NextApiResponse) {
  const svg = render(req.query);
  res.setHeader('Content-Type', 'image/svg+xml');
  res.end(svg);
}

Redirects

This is an optional step, but I decided to add redirects in my Next.js config to avoid the api/ part in the URLs:

next.config.js
async rewrites() {
  return [
    // Hide /api from an image URL
    {
      source: '/world-globe.svg',
      destination: '/api/world-globe.svg',
    },
    {
      source: '/world-map.svg',
      destination: '/api/world-map.svg',
    },
  ];
},

Interactions

Webmentions

Like this content? Buy me a coffeeor share around:

0 Like

0 Reply & Share