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:
- proxy the request to the same url
- 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.