Creating a simple access middleware for nextjs route handlers

In the past days I've been working into a simple demo app to understand how to work between a web application made with nextjs and a mobile app in flutter. I'm not a big fan of the solutions provided by nextjs regarding the middleware aspect of the framework, instead I prefer the approach of laravel or express. With that in mind I decide to build a wrapper function to add protection for specific endpoints. So the first thing that I did was move my endpoints logic from a route.ts file to a controller.ts. Second we create a simple function to receive the permission that the user should be have to access that endpoint, the Request and Params for the endpoint and finally the callback function to be called. Wrapper function for the endpoints import { NextRequest } from "next/server" import { getToken } from "./get-token" /** * Helper function to wrap the route handler methods to allow to check if the user * has the permission to access the endpoint. */ export const hasAccess = async ( permission: string, req: NextRequest, params: any, method: Function ) => { const token = await getToken(req) if (!token) return Response.json({ message: "Unauthorized" }, { status: 401 }) if (!token.permissions?.includes(permission)) return Response.json({ message: "Forbidden" }, { status: 403 }) return method(req, params) } Example of the endpoints route.ts import { GET as getItems, POST as createItem } from "./controller" import { hasAccess } from "@/helpers/has-access" import { PERMISSIONS } from "@/constants/permissions" import { NextRequest } from "next/server" const GET = (req: NextRequest, params: any) => hasAccess(PERMISSIONS.items.getItems, req, params, getItems) const POST = (req: NextRequest, params: any) => hasAccess(PERMISSIONS.items.createItem, req, params, createItem) export { GET, POST } Controller.ts import { NextRequest } from "next/server" import { itemSchema } from "./schema" import { pgQuery } from "@/lib/pg" import { itemsQuery } from "./queries" import getItems from "@/actions/items/get-items" export async function GET() { try { const items = await getItems() return Response.json(items) } catch (error) { console.error(error) return Response.json({ error: "Something went wrong" }, { status: 500 }) } } export async function POST(req: NextRequest) { try { const json = await req.json() const validated = itemSchema.parse(json) await pgQuery(itemsQuery.createOne, [validated.name, validated.description]) return Response.json({ success: "Item created" }, { status: 201 }) } catch (error) { console.error(error) return Response.json({ error: "Something went wrong" }, { status: 500 }) } } I want to share this as a random tip for when you want to build simple demos and don't want to go with a more robust solution. After all the goal of this project is to learn how to communicate a mobile app and a web app, using the frameworks of flutter and nextjs.

Apr 15, 2025 - 02:06
 0
Creating a simple access middleware for nextjs route handlers

In the past days I've been working into a simple demo app to understand how to work between a web application made with nextjs and a mobile app in flutter.

I'm not a big fan of the solutions provided by nextjs regarding the middleware aspect of the framework, instead I prefer the approach of laravel or express.

With that in mind I decide to build a wrapper function to add protection for specific endpoints.

So the first thing that I did was move my endpoints logic from a route.ts file to a controller.ts.

Second we create a simple function to receive the permission that the user should be have to access that endpoint, the Request and Params for the endpoint and finally the callback function to be called.

Wrapper function for the endpoints

import { NextRequest } from "next/server"
import { getToken } from "./get-token"

/**
 * Helper function to wrap the route handler methods to allow to check if the user
 * has the permission to access the endpoint.
 */
export const hasAccess = async (
    permission: string,
    req: NextRequest,
    params: any,
    method: Function
) => {
    const token = await getToken(req)

    if (!token) return Response.json({ message: "Unauthorized" }, { status: 401 })

    if (!token.permissions?.includes(permission))
        return Response.json({ message: "Forbidden" }, { status: 403 })

    return method(req, params)
}

Example of the endpoints

route.ts

import { GET as getItems, POST as createItem } from "./controller"
import { hasAccess } from "@/helpers/has-access"
import { PERMISSIONS } from "@/constants/permissions"
import { NextRequest } from "next/server"

const GET = (req: NextRequest, params: any) =>
    hasAccess(PERMISSIONS.items.getItems, req, params, getItems)

const POST = (req: NextRequest, params: any) =>
    hasAccess(PERMISSIONS.items.createItem, req, params, createItem)

export { GET, POST }

Controller.ts

import { NextRequest } from "next/server"
import { itemSchema } from "./schema"
import { pgQuery } from "@/lib/pg"
import { itemsQuery } from "./queries"
import getItems from "@/actions/items/get-items"

export async function GET() {
    try {
        const items = await getItems()
        return Response.json(items)
    } catch (error) {
        console.error(error)
        return Response.json({ error: "Something went wrong" }, { status: 500 })
    }
}

export async function POST(req: NextRequest) {
    try {
        const json = await req.json()
        const validated = itemSchema.parse(json)

        await pgQuery(itemsQuery.createOne, [validated.name, validated.description])

        return Response.json({ success: "Item created" }, { status: 201 })
    } catch (error) {
        console.error(error)
        return Response.json({ error: "Something went wrong" }, { status: 500 })
    }
}

I want to share this as a random tip for when you want to build simple demos and don't want to go with a more robust solution. After all the goal of this project is to learn how to communicate a mobile app and a web app, using the frameworks of flutter and nextjs.