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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@api-typer/core

v0.1.1

Published

Define Express routes with Zod schemas and auto-generate a typed React Query client and OpenAPI spec

Readme

api-typer

End-to-end type-safe APIs for Express — with OpenAPI and TanStack Query (React Query) built in.

Define your routes once. Get request validation, an OpenAPI spec, and a fully typed TanStack Query client — automatically.

npm version license


Introduction

Building a REST API with Express means writing the route, then the types, then the fetch call, then keeping all three in sync. api-typer eliminates that.

You define an Express route with a Zod schema. api-typer validates every incoming request, generates a standards-compliant OpenAPI spec, and produces a fully typed TanStack Query (React Query) client — complete with hooks, direct calls, and cache helpers.

Your route definition is the contract. No separate package. No code duplication. No drift.


Highlights

  • 🔗 End-to-end type safety — inputs, outputs, and errors are fully typed from server to client
  • TanStack Query (React Query) client — typed hooks, direct calls, and cache helpers generated automatically
  • 📘 First-class OpenAPI — generates a standards-compliant OpenAPI 3.0 spec from your route definitions
  • Automatic validation — Zod schemas validated on every request, 422 with structured errors on failure
  • 🛟 Type-safe responsesrespond('ok', data) is typed to the exact schema you defined
  • 🌿 Express-native — drop into any existing Express app, no new framework to learn
  • 🚦 Route conflict detection — throws at startup if a route would be shadowed by another
  • 🔌 Zero frontend coupling — the generated client has no runtime dependency on api-typer
  • 👀 Examples — check out the examples/ folder to get started quickly

Try it out

Clone the repo and run the example:

git clone https://github.com/inas-sirhan/api-typer.git
cd api-typer
npm install
npx tsx examples/basic.ts

This generates artifacts/openapi.json and artifacts/api.ts from the example routes.


Install in your project

npm install @api-typer/core

More documentation coming soon.


Quick start

1. Define routes

import express from 'express';
import * as z from 'zod';
import { createApiBuilder, createSuccessResponse, createErrorResponse } from '@api-typer/core';

const app = express();
app.use(express.json());

const { route, sync } = createApiBuilder(app);

route(
    {
        operationId: 'get-user',
        method: 'get',
        path: '/users/:id',
        summary: 'Get a user by ID',
        tags: ['users'],
        request: z.object({ id: z.string() }),
        responses: {
            ok: createSuccessResponse({
                statusCode: 200,
                schema: z.object({ id: z.string(), name: z.string() }),
            }),
            notFound: createErrorResponse({
                statusCode: 404,
                errorCode: 'USER_NOT_FOUND',
            }),
        },
    },
    async ({ input, respond, req, res }) => {
        // input, respond — typed to your schema
        // req, res — raw Express objects, available when you need them
        const user = await db.findUser(input.id);
        if (!user) return respond('notFound');
        respond('ok', user);
    },
);

await sync({ serverUrl: 'http://localhost:3000' });

app.listen(3000);

2. What gets generated

Outputs to ./artifacts/:

| File | Description | |------|-------------| | openapi.json | OpenAPI 3.0 spec | | api.ts | Typed React Query hooks + Api namespace | | axios.ts | Pre-configured Axios instance (generated once, yours to edit — add interceptors, etc.) |

3. Use in React

import { Api, useGetGetUserQueryData, useSetGetUserQueryData } from './artifacts/api';

// Params, response data, and errors are all fully typed
const { data: user, error } = Api.useGetUser({ id: '123' });

// Cache read — typed to the exact response schema
const getUserData = useGetGetUserQueryData();
getUserData({ id: '123' });

// Cache write — updater is typed, wrong shape is a compile error
const setUserData = useSetGetUserQueryData();
setUserData({ id: '123' }, updatedUser);

If you rename a route or change its request or response shape, TypeScript will surface every broken call in your frontend.


How it works

route()
  │
  ├─▶  Express handler  (Zod validation on every request)
  │
  └─▶  sync()
         │
         ├─▶  openapi.json  (OpenAPI 3.0 spec)
         │
         └─▶  api.ts  (React Query hooks via Orval)
                │
                └─▶  Api.useGetUser(...)
                     Api.createUser(...)
                     Api.deleteUser(...)

API Reference

createApiBuilder(app, options?)

| Option | Type | Default | |--------|------|---------| | prefix | string | '/api' |

Returns { route, sync }.


route(config, handler)

| Field | Type | Description | |-------|------|-------------| | operationId | string | Unique kebab-case ID (e.g. 'get-user') | | method | 'get' \| 'post' \| 'put' \| 'patch' \| 'delete' | HTTP method | | path | string | Express path (e.g. '/users/:id') | | request | ZodObject | Schema for path params + body/query | | responses | Record<string, ResponseDef> | Keyed response definitions | | middlewares? | RequestHandler[] | Express middlewares to run before the handler | | summary? | string | OpenAPI summary | | description? | string | OpenAPI description | | tags? | string[] | OpenAPI tags |


sync(options?)

| Option | Type | Default | |--------|------|---------| | openApiOutput | string | './artifacts' | | clientOutput | string | './artifacts' | | serverUrl | string | 'http://localhost:3000' |


Response helpers

// Success with a response body
createSuccessResponse({ statusCode, schema, description? })

// Error with a standard { errorCode: string } body
createErrorResponse({ statusCode, errorCode, description? })

// 204 No Content
createSuccessResponseNoContent({ description? })

License

MIT


v0.1.3 — early release. A more robust and feature-rich version will be released soon — stay tuned.