zod-endpoint
TypeScript icon, indicating that this package has built-in type declarations

2.0.0 • Public • Published

zod-endpoint

Extremely lightweight zod and express integration enabling type-safe HTTP endpoints.

Both express and zod are unopinionated micro libraries - and we inherit and embrace the flexibility of both.

Usage

zod-endpoint can be used client-side or server-side (or both).

It is easy to incrementally adopt in your project if you are already using express (server-side) or axios (client-side).

Specification

First step is to define a specification of an endpoint

import { endpoint, path } from "zod-endpoint/spec";

// Define the endpoint specification
const getPostsEndpoint = endpoint({

    // Specify the HTTP method
    method: "get",

    // Define the path /posts/:id through a type-safe fluent builder
    path: path()
        .literal("posts")
        .placeholder("id", z.string())
        .build(),

    // Define the shape of parsed Query parameters
    query: z.object({
        include: z.string().optional(),
        tag: z.string(),
    }),

    // Define the shape of data returned by service
    result: z.object({
        id: z.string(),
        tags: z.array(z.string()),
        name: z.string(),
    }),
});

Server

While implementing the server, we can bridge this specification to an endpoint which implements our service.

import express from "express";
import { bridge } from "zod-endpoint/server";

const app = express();

// Bridge the endpoint spec to a service which is independent
// of http
bridge(getPostsEndpoint)
.through({
    // We receive a zod type that can be used to parse 
    // the incoming request. 
    //
    // We augment this type with transformations to generate the
    // input our service expects.
    inputType: (it) =>
        it.transform((it) => ({
            id: it.params.id,
            tag: it.query.tag,
        })),

    // Zod-type to transform the service output
    // to http output
    outputType: (it) => it,
})

    // Implementation of our service
    //
    // Note that this service implementation does not receive
    // request/response object and is http independent.
    .toService(async ({ id, tag }) => {

        // Dummy implementation.
        // In a real application we will most likely fetch this data
        // from some data store
        const tags: string[] = [];
        if (tag) tags.push(tag);
        return {
            id,
            tags,
            name: "Test Post",
        };
    })

    // Attach our middleware to express router
    .attach(app);

const server = app.listen(3000);

Client

We can also create a client from this spec

import { request } from "zod-endpoint/client";

const getPost = request(getPostsEndpoint);

const resp = await getPost({
    params: {
        id: "1",
    },
    query: {
        tag: "foo",
    }
});

Note that we don't have to implement both the Server & Client implementations.

So we can still use zod-endpoint if either our server or client is not in our control. However using it in both client and server provides us end-to-end type-safety without needing to redeclare any types or needing any code-generation.

We are also able to implement any HTTP API - zod-endpoint is unassuming and does not restrict us to implement some custom protocol with adhoc limitations.

We also don't necessarily need to accept or return JSON - it is perfectly fine to use this for applications that return HTML or binary content.

Package Sidebar

Install

npm i zod-endpoint

Weekly Downloads

1

Version

2.0.0

License

MIT

Unpacked Size

53.9 kB

Total Files

19

Last publish

Collaborators

  • lorefnon