npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

zod-endpoint

v2.0.0

Published

Lightweight zod and express integration enabling type-safe HTTP endpoints

Downloads

11

Readme

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.