Skip to content

venables/typed-route-handler

Repository files navigation

typed-route-handler

Type-safe Route Handlers for Next.js
import { NextResponse } from "next/server"
import type { Handler } from 'typed-route-handler'

type ResponseData = {
  result: string
  over: number
}

export const GET: Handler<ResponseData> = async (req) => {
  return NextResponse.json({
    result: "this response is type-checked",
    over: 9000
  })
}

Features

  • Type-safe route handler responses
  • Type-safe route handler parameters
  • ✅ Full zod (v3 and v4) and valibot compatibility
  • ✅ Production ready

Installation

npm i typed-route-handler

Note

This library may be installed as a devDependency when only used for types. If you'd like to use the param parsers or the route handler wrapper function, you can install it as a regular dependency.

Usage

The typed handler is easy to use: In the simplest case, just add the type Handler to your route handler and it will automatically assign types.

+ import type { Handler } from 'typed-route-handler'

- export const GET = async (req: NextRequest) => {
+ export const GET: Handler = async (req) => {
    // ...
  }

Typed Responses

The real magic comes when you add typing to your responses.

import { NextResponse } from "next/server"
import type { Handler } from 'typed-route-handler'

type ResponseData = {
  name: string
  age: number
}

export const GET: Handler<ResponseData> = (req) => {
  return NextResponse.json({
    name: "Bluey",
    age: "seven", // <-- this will cause a type error
  })
}

Typed Parameters

We can also add type verification to our parameters:

// app/api/[name]/route.ts
import { NextResponse } from "next/server"
import { type Handler } from "typed-route-handler"

type ResponseData = {
  name: string
}

type Params = {
  name: string
}

export const GET: Handler<ResponseData, Params> = async (req, { params }) => {
  const { name } = await params // <-- this will mark `name` as a string

  return NextResponse.json({
    name
  })
}

Param Parsers

Adding compile-time param checking is nice, but it does not perform any runtime type-checking which is more important for parameters. To do that, you can use the included zod (v3 and v4) or valibot parseParams method:

import { NextResponse } from "next/server"
import { parseParams } from 'typed-route-handler/zod'
import * as z from 'zod/v4'
import { type Handler } from "typed-route-handler"

type ResponseData = {
  id: number
  url: string
}

const paramsSchema = z.object({
  id: z.coerce.number(),
  url: z.string().url()
})

export const GET: Handler<ResponseData> = async (req, ctx) => {
  const { id, url } = await parseParams(ctx, paramsSchema)

  return NextResponse.json({
    id, // a number, coerced from a string
    url // a url
  })
}

You can also use the safeParseParams method to prevent throwing exceptions if the params are not valid.

For different schema validators you can use the following imports:

Library Import Path
zod v4 import { parseParams } from 'typed-route-handler/zod'
zod v3 import { parseParams } from 'typed-route-handler/zod/v3'
valibot import { parseParams } from 'typed-route-handler/valibot'

Wrapper function

In addition, the library also provides a convenience wrapper function handler which simply applies the Handler type to the function. Since this is a no-op, it is recommended to use the Handler type directly.

import { handler } from "typed-route-handler"
import { NextResponse } from 'next/server'

type ResponseBody = {
  balance: number
}

export const GET = handler<ResponseBody>(async (req) => {
  return NextResponse.json({
    balance: 9_000
  })
})

Usage with modified reqs (e.g. next-auth)

When using this library with next-auth or other libraries which modify the req objects, you can pass a 3rd type to the handler call, representing modified Request object type. For example:

import { auth } from '@/auth'
import { type NextAuthRequest } from 'next-auth'
import { handler } from 'typed-route-handler'

type Params = { id: string }

export const GET = auth(
  handler<ResponseBody, Params, NextAuthRequest>((req, ctx) => {
    if (!req.auth?.user) {
      unauthorized()
    }

    // ...
  })
)

🏰 Production Ready

Already widely used in high-traffic production apps at Catena Labs, songbpm, jog.fm, usdc.cool, as well as all StartKit next.js projects.

Open Source

This project is MIT-licensed and is free to use and modify for your own projects.

It was created by Matt Venables.

About

Type-safe API Route Handlers for Next.js

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •