joeltg/next-rest

Type guard with Zod

Closed this issue · 1 comments

Really nice work on next-rest @joeltg !

Quick question, what is the equivalent of that with zod ?

headers: getRequestHeaders.is,
body: getRequestBody.is,

Come from this example:

import * as t from "io-ts"

const getRequestHeaders = t.type({ accept: t.literal("application/json") })
const getRequestBody = t.void

export default makeHandler("/api/widgets/[id]", {
  GET: {
    headers: getRequestHeaders.is,
    body: getRequestBody.is,
    exec: async ({ params }) => {
      // ... method implementation
      return {
        headers: { "content-type": "application/json" },
        body: { id: params.id, isGizmo: false },
      }
    },
  },
})

There is no built-in type guard function in zod like io-ts with is.

So I made a small helper for zod:

// zod-check.ts
import { ZodSchema } from "zod"

export function check<T>(schema: ZodSchema<T>) {
  function is(data: unknown): data is T {
    return schema.safeParse(data).success
  }
  return { is }
}

I change these lines:

+ import { check } from "./zod-check"

- headers: getRequestHeaders.is,
- body: getRequestBody.is,
+ headers: check(getRequestHeaders).is,
+ body: check(getRequestBody).is,

Now the full code that use zod validation:

import { ZodSchema } from "zod"
import { check } from "./zod-check"

const getRequestHeaders = z.object({ accept: z.literal("application/json") })
const getRequestBody = z.void()

export default makeHandler("/api/widgets/[id]", {
  GET: {
    headers: check(getRequestHeaders).is,
    body: check(getRequestBody).is,
    exec: async ({ params }) => {
      // ... method implementation
      return {
        headers: { "content-type": "application/json" },
        body: { id: params.id, isGizmo: false },
      }
    },
  },
})