Using Nextjs Middleware for Geolocation Detection

Published: Sun Jan 23 2022

Middleware was introduced in v12 of nextjs. It enables us to run some logic on a request prior to it completing and to share that logic between multiple pages. Among the common use cases for middleware includes the subject of this post: geolocation detection.

Context

I'd been using an external ip lookup service in one of my prjects to determine and serve content based the user's location. With the introduction of nextjs middleware, I realised that I could use the new geo object on NextResponse, which has the following properties:

geo.country - The country code
geo.region - The region code
geo.city - The city
geo.latitude - The latitude
geo.longitude - The longitude

This is great since all of this information was helpful to my specific use case. Next, I needed to write the middleware and figure out how to get this data into one of my pages.

The middleware

I created the middleware in a file titled _middleware.ts and added into into my pages folder since I wanted its logic to be scoped to the pages in the pages folder. Here is a simplified version of the contents:

import { NextRequest, NextResponse } from 'next/server'

export async function middleware(req: NextRequest) {
  const { nextUrl: url, geo } = req
  const { country, city, latitude, longitude } = geo

  url.searchParams.set('country', country || 'FI')
  url.searchParams.set('city', city || 'Helsinki')
  url.searchParams.set('latitude', latitude || '60.1708')
  url.searchParams.set('longitude', longitude || '24.9375')

  return NextResponse.rewrite(url)
}

NB: One gotcha here is that since these middleware functions are run on an edge runtime, the geo object is empty until the function has been deployed to the edge. In other words, it won't work when running next dev and next build environment.

Getting geodata data from middleware to a page

The approach above uses a request rewrite to essentially:

  1. proxy the request to the same url
  2. add the populated searchParams to the query object

The user does not see any change in the url, but we've been able to pack our information into the query object. We can then access the query object in getServerSideProps and pass it to the page through props like so:

export const getServerSideProps = ({ query }) => ({
  props: query,
})

Conclusion

I see the potential for next.js middleware, but I hope that we eventually get a cleaner way of passing data from middleware to pages.