0.0.45 • Published 10 months ago

next-zod-router v0.0.45

Weekly downloads
-
License
-
Repository
-
Last release
10 months ago

next-zod-router

A library for simple API routing in Next.js while leveraging Zod and Typescript to create typesafe routes and middlewares with built in validation.

npm.io

Motivation

I wanted to create type-safe APIs in Next.js using zod and also wanted to generate type definition files for client-side use so that I could use intuitive API calls. But I couldn't find a library that met my needs, so I created this library.

Features

  • Type-safe API routing
  • Type-safe API call
  • Validation using zod
  • Error handling
  • Type definition file generation for client-side use
  • Middleware support

Demo

https://stackblitz.com/edit/next-typescript-32qrbx?embed=1&file=pages/index.tsx&file=pages/api/sample/[id].ts&hideNavigation=1&view=editor

Usage

Installation

## npm
npm install next-zod-router

## yarn
yarn add next-zod-router

Server-side

  1. Use zod to define the types for body, query, and res.
  2. Create routing handling with createRouter.
  3. Assign types to the created routing handling with validate.
  4. Export the types as GetHandler and PostHandler.
// pages/api/sample.ts
import { ApiHandler, createRouter, validate } from "next-zod-router";
import { z } from "zod";

/* Schema definition using zod */
const postValidation = {
  body: z.object({
    foo: z.string(),
  }),
  query: z.object({
    bar: z.string().optional(),
  }),
  res: z.object({
    message: z.string(),
  }),
}

const getValidation = {
  query: z.object({
    bar: z.string().optional(),
  }),
  res: z.object({
    message: z.string(),
  }),
}    

/* Routing */
const router = createRouter()

router
  .use((req, res, next) => {
    console.log("middleware");
    return next()
  })
  .post(
    validate(postValidation),
    (req, res) => {
      req.body.foo;
      req.query.bar;
      res.status(200).json({ message: "ok" });
    })
  .get(
    validate(getValidation),
    (req, res) => {
      req.query.bar;
      res.status(200).json({ message: "ok" });
    })

/* Type export */
// the export type name should be as follows
// so that the type definition file can be generated correctly via the command.
export type PostHandler = ApiHandler<typeof postValidation>
export type GetHandler = ApiHandler<typeof getValidation>

/* Routing handling export */
export default router.run()

Type generation

## npm
npx next-zod-router

## yarn
yarn next-zod-router

Adding a script to your package.json is convenient.

{
  "scripts": {
    "apigen": "next-zod-router"
  }
}
npm run apigen

Client-side

import { client } from "next-zod-router";

// Type-safe API call
const { data, error } = await client.post("/api/sample", {
  query: {
    bar: "baz",
  },
  body: {
    foo: "bar",
  },
})

dynamic routing

Server-side

// pages/api/[id].ts

const getValidation = {
  // 👇 for server side validation
  // 👇 also necessary for client side url construction
  query: z.object({
    id: z.string().optional(),
  }),
}

router
  .get(
    validate(getValidation),
    (req, res) => {
      req.query.id;
      res.status(200).json({ message: "ok" });
    })

Client-side

// client.ts
import { client } from "next-zod-router";

client.get("/api/[id]", {
  query: {
    id: "1",
  },
})

// url will be /api/1

Error handling

throw error

// pages/api/sample.ts
router
  .post(
    validate(postValidation),
    (req, res) => {
      const session = getSession(req)
      if (!session) {
        throw createError(401, "Unauthorized")
      }
      res.status(200).json({ message: "ok" });
    })

custom error handling

// pages/api/sample.ts

router
  .onError((err, req, res) => {
    // custom error handling
    res.status(err.statusCode).json({ message: err.message });
  })

Middleware

express-like middleware is supported.

// pages/api/sample.ts
import cors from "cors";
import { createRouter, validate } from "next-zod-router";
import { z } from "zod";

const postValidation = {
  res: z.object({
    message: z.string(),
  }),
}

router
  .use(cors())
  .post(
    validate(postValidation),
    (req, res) => {
      res.status(200).json({ message: "ok" });
    })

Command options

The default pages directory is pages, so if you want to change it, you can use the --pagesDir option.

next-zod-router --pagesDir=src/pages
OptionDescriptionDefault value
--pagesDirPages directorypages
--baseDirProject directory.
--distDirType definition file output destinationnode_modules/.next-zod-router
--moduleNameSpaceType definition file module name.next-zod-router
--watchWatch modefalse

Tips

Add session property to Request type

If you want to add session property to Request type, you can use the following code.

// global.d.ts
import { IncomingMessage } from "http";

declare module 'next' {
  export interface NextApiRequest extends IncomingMessage {
    session: Session
  }
}

Next.js development

When developing with Next.js, you can use the following code to generate type definition files automatically.

{
  "scripts": {
    "dev": "npm-run-all -p dev:*",
    "dev:next": "next dev",
    "dev:apigen": "next-zod-router -w"
  }
}
0.0.45

10 months ago

0.0.44

12 months ago

0.0.43

1 year ago

0.0.42

1 year ago

0.0.40

1 year ago

0.0.39

1 year ago

0.0.38

1 year ago

0.0.37

1 year ago

0.0.36

1 year ago

0.0.35

1 year ago

0.0.34

1 year ago