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

transit-kit

v0.9.0

Published

A declarative TypeScript framework for building type-safe Express.js APIs with automatic OpenAPI generation

Readme

Transit-Kit

CI Coverage Status npm version License: MIT TypeScript Node.js

A declarative TypeScript framework for building end-to-end type-safe REST APIs with Express.js and automatic OpenAPI documentation generation.

Why Transit-Kit?

Transit-Kit brings modern type-safety and developer experience to Express.js without abandoning the familiar, battle-tested foundation you already know. It's built on minimalism:

  • 🎯 Express at its core — Use the familiar Express API you already know
  • 🔒 End-to-end type safety — Full TypeScript inference from request to response
  • 📝 Auto-generated OpenAPI — Documentation that stays in sync with your code
  • Zod validation — Runtime type checking with zero boilerplate
  • 🪶 Minimal overhead — Thin layer over Express, not a complete rewrite
  • Declarative definitions — Define endpoints declaratively with full type info

Requirements

  • Node.js 22+
  • TypeScript 5.9+

Installation

npm install transit-kit
yarn add transit-kit
pnpm add transit-kit

Quick Start

import { createServer } from "transit-kit/server";
import { createApiEndpointHandler } from "transit-kit/server";
import { z } from "zod";

// Create a server
const server = createServer({
  port: 3000,
});

// Define a simple endpoint
const helloEndpoint = createApiEndpointHandler(
  {
    meta: {
      name: "sayHello",
      group: "Greetings",
      description: "Returns a greeting message",
    },
    path: "/hello/:name",
    method: "get",
    responseSchemas: {
      200: {
        type: "json",
        schema: z.object({
          message: z.string(),
        }),
      },
    },
    securitySchemes: [],
  },
  async ({ parameters }) => {
    return {
      code: 200,
      data: {
        message: `Hello, ${parameters.name}!`,
      },
    };
  }
);

// Register and start
server.registerApiEndpoint(helloEndpoint);
server.start();

Core Concepts

1. Endpoint Definitions

Every API endpoint in Transit-Kit is defined declaratively with full type information:

const definition = {
  meta: {
    name: "createUser",           // Unique operation ID
    group: "Users",                // OpenAPI tag
    description: "Create a user"  // Endpoint description
  },
  path: "/users",                  // Express-style path
  method: "post",                  // HTTP method
  requestBodySchema: z.object({    // Request validation (optional)
    name: z.string(),
    email: z.string().email(),
  }),
  querySchema: z.object({          // Query params validation (optional)
    sendEmail: z.boolean().optional(),
  }),
  responseSchemas: {               // All possible responses
    201: {
      type: "json",
      schema: z.object({
        id: z.string(),
        name: z.string(),
        email: z.string(),
      }),
    },
    400: {
      type: "json",
      schema: z.object({
        error: z.string(),
      }),
    },
  },
  securitySchemes: [],             // Auth schemes (if any)
};

2. Type-Safe Handlers

Handlers automatically infer types from your endpoint definition:

const handler = async ({ body, query, parameters }) => {
  // body is typed as { name: string, email: string }
  // query is typed as { sendEmail?: boolean }
  // parameters are inferred from the path pattern
  
  const user = await createUserInDatabase(body);
  
  return {
    code: 201,
    data: user,  // Must match the response schema for code 201
  };
};

3. Server Registration

Combine definitions and handlers, then register with your server:

const endpoint = createApiEndpointHandler(definition, handler);
server.registerApiEndpoint(endpoint);

Example: CRUD API

Here's a complete example showing a REST API for managing todos:

import { createServer, createApiEndpointHandler } from "transit-kit/server";
import { z } from "zod";

const server = createServer({
  port: 3000,
});

// In-memory storage (use a real database in production)
const todos: Map<string, { id: string; title: string; completed: boolean }> = new Map();

// Schemas
const TodoSchema = z.object({
  id: z.string(),
  title: z.string(),
  completed: z.boolean(),
});

const CreateTodoSchema = z.object({
  title: z.string().min(1),
});

// CREATE
const createTodo = createApiEndpointHandler(
  {
    meta: {
      name: "createTodo",
      group: "Todos",
      description: "Create a new todo item",
    },
    path: "/todos",
    method: "post",
    requestBodySchema: CreateTodoSchema,
    responseSchemas: {
      201: {
        type: "json",
        schema: TodoSchema,
      },
    },
    securitySchemes: [],
  },
  async ({ body }) => {
    const id = crypto.randomUUID();
    const todo = {
      id,
      title: body.title,
      completed: false,
    };
    todos.set(id, todo);

    return {
      code: 201,
      data: todo,
    };
  }
);

// READ (List)
const listTodos = createApiEndpointHandler(
  {
    meta: {
      name: "listTodos",
      group: "Todos",
      description: "Get all todo items",
    },
    path: "/todos",
    method: "get",
    responseSchemas: {
      200: {
        type: "json",
        schema: z.object({
          todos: z.array(TodoSchema),
        }),
      },
    },
    securitySchemes: [],
  },
  async () => {
    return {
      code: 200,
      data: {
        todos: Array.from(todos.values()),
      },
    };
  }
);

// READ (Single)
const getTodo = createApiEndpointHandler(
  {
    meta: {
      name: "getTodo",
      group: "Todos",
      description: "Get a specific todo item",
    },
    path: "/todos/:id",
    method: "get",
    responseSchemas: {
      200: {
        type: "json",
        schema: TodoSchema,
      },
      404: {
        type: "json",
        schema: z.object({ error: z.string() }),
      },
    },
    securitySchemes: [],
  },
  async ({ parameters }) => {
    const todo = todos.get(parameters.id);
    
    if (!todo) {
      return {
        code: 404,
        data: { error: "Todo not found" },
      };
    }

    return {
      code: 200,
      data: todo,
    };
  }
);

// UPDATE
const updateTodo = createApiEndpointHandler(
  {
    meta: {
      name: "updateTodo",
      group: "Todos",
      description: "Update a todo item",
    },
    path: "/todos/:id",
    method: "put",
    requestBodySchema: z.object({
      title: z.string().optional(),
      completed: z.boolean().optional(),
    }),
    responseSchemas: {
      200: {
        type: "json",
        schema: TodoSchema,
      },
      404: {
        type: "json",
        schema: z.object({ error: z.string() }),
      },
    },
    securitySchemes: [],
  },
  async ({ parameters, body }) => {
    const todo = todos.get(parameters.id);
    
    if (!todo) {
      return {
        code: 404,
        data: { error: "Todo not found" },
      };
    }

    const updated = {
      ...todo,
      ...body,
    };
    todos.set(parameters.id, updated);

    return {
      code: 200,
      data: updated,
    };
  }
);

// DELETE
const deleteTodo = createApiEndpointHandler(
  {
    meta: {
      name: "deleteTodo",
      group: "Todos",
      description: "Delete a todo item",
    },
    path: "/todos/:id",
    method: "delete",
    responseSchemas: {
      204: {
        type: "json",
        schema: z.object({}),
      },
      404: {
        type: "json",
        schema: z.object({ error: z.string() }),
      },
    },
    securitySchemes: [],
  },
  async ({ parameters }) => {
    const exists = todos.has(parameters.id);
    
    if (!exists) {
      return {
        code: 404,
        data: { error: "Todo not found" },
      };
    }

    todos.delete(parameters.id);

    return {
      code: 204,
      data: {},
    };
  }
);

// Register all endpoints
server.registerApiEndpoint(createTodo);
server.registerApiEndpoint(listTodos);
server.registerApiEndpoint(getTodo);
server.registerApiEndpoint(updateTodo);
server.registerApiEndpoint(deleteTodo);

server.start();
console.log("Server running on http://localhost:3000");

Authentication

Transit-Kit supports Basic and Bearer authentication schemes out of the box.

Basic Authentication

import { createBasicAuthSchema } from "transit-kit/server";

// Define your user type
interface User {
  id: string;
  username: string;
  role: string;
}

// Create an auth scheme
const basicAuth = createBasicAuthSchema<User>(
  "basicAuth",
  async (username, password) => {
    // Validate credentials (use your database)
    if (username === "admin" && password === "secret") {
      return {
        id: "1",
        username: "admin",
        role: "admin",
      };
    }
    return null; // Invalid credentials
  }
);

Bearer Authentication

import { createBearerAuthSchema } from "transit-kit/server";

const bearerAuth = createBearerAuthSchema<User>(
  "bearerAuth",
  async (token) => {
    // Validate token (e.g., JWT verification)
    const user = await verifyJWT(token);
    return user; // or null if invalid
  }
);

Protected Endpoints

const protectedEndpoint = createApiEndpointHandler(
  {
    meta: {
      name: "getProfile",
      group: "Users",
      description: "Get current user profile",
    },
    path: "/profile",
    method: "get",
    responseSchemas: {
      200: {
        type: "json",
        schema: z.object({
          id: z.string(),
          username: z.string(),
          role: z.string(),
        }),
      },
      401: {
        type: "json",
        schema: z.object({ error: z.string() }),
      },
    },
    securitySchemes: [basicAuth], // Require authentication
  },
  async ({ caller }) => {
    // caller is typed as User | null
    if (!caller) {
      return {
        code: 401,
        data: { error: "Unauthorized" },
      };
    }

    return {
      code: 200,
      data: caller, // Fully typed user object
    };
  }
);

server.registerApiEndpoint(protectedEndpoint);

Multiple Auth Schemes

You can support multiple authentication methods:

const endpoint = createApiEndpointHandler(
  {
    // ... other config
    securitySchemes: [basicAuth, bearerAuth], // Accept either
  },
  async ({ caller }) => {
    // caller will be set if any scheme succeeds
    // ...
  }
);

OpenAPI Documentation

Transit-Kit automatically generates OpenAPI 3.0 documentation from your endpoint definitions.

import { generateOpenApiDoc } from "transit-kit/generator";

const openApiDoc = await generateOpenApiDoc(server, {
  title: "My API",
  version: "1.0.0",
  description: "A type-safe REST API built with Transit-Kit",
  servers: [
    {
      url: "http://localhost:3000",
      description: "Development server",
    },
    {
      url: "https://api.example.com",
      description: "Production server",
    },
  ],
  contact: {
    name: "API Support",
    email: "[email protected]",
  },
  license: {
    name: "MIT",
    url: "https://opensource.org/licenses/MIT",
  },
});

// Serve the OpenAPI spec
server.expressApp.get("/openapi.json", (req, res) => {
  res.json(openApiDoc);
});

// Or write to file
import { writeFileSync } from "fs";
writeFileSync("./openapi.json", JSON.stringify(openApiDoc, null, 2));

The generated OpenAPI document includes:

  • All endpoints with request/response schemas
  • Path and query parameters
  • Request body validation schemas
  • Security requirements
  • Response schemas for all status codes

You can use this with tools like:

  • Swagger UI for interactive documentation
  • Postman for API testing
  • OpenAPI Generator for client SDK generation

API Reference

Server

createServer(config: ServerConfig): Server

Creates a new Transit-Kit server instance.

Config Options:

  • port: number — Port to listen on

Returns: Server instance with:

  • expressApp: Application — Underlying Express app
  • registerApiEndpoint(endpoint) — Register an API endpoint
  • start() — Start the server
  • endpointDefinitions — Array of registered endpoint definitions

Endpoint Creation

createApiEndpointHandler(definition, handler)

Creates a type-safe API endpoint.

Parameters:

  • definition — Endpoint definition object
  • handler — Async function handling the request

Handler receives:

  • request — Express Request object
  • response — Express Response object
  • parameters — Type-safe path parameters
  • query — Type-safe query parameters
  • body — Type-safe request body
  • caller — Authenticated user (if auth is enabled)

Authentication

createBasicAuthSchema<Caller>(name, validateCaller)

Creates a Basic authentication scheme.

Parameters:

  • name: string — Unique name for the scheme
  • validateCaller: (username, password) => Promise<Caller | null> — Validation function

createBearerAuthSchema<Caller>(name, validateCaller)

Creates a Bearer token authentication scheme.

Parameters:

  • name: string — Unique name for the scheme
  • validateCaller: (token) => Promise<Caller | null> — Validation function

OpenAPI Generation

generateOpenApiDoc(server, options): Promise<OpenAPIV3.Document>

Generates OpenAPI 3.0 documentation.

Parameters:

  • server: Server — Your Transit-Kit server instance
  • options: GeneratorOptions — OpenAPI metadata

Options:

  • title: string — API title
  • version: string — API version
  • description?: string — API description
  • servers?: ServerObject[] — Server URLs
  • contact?: ContactObject — Contact information
  • license?: LicenseObject — License information

Response Types

Transit-Kit currently supports JSON responses:

{
  type: "json",
  schema: z.object({ /* your schema */ }),
  headers?: ["X-Custom-Header"], // Optional custom headers
}

When using custom headers in your response schema, you must include them in the handler response:

return {
  code: 200,
  data: { /* ... */ },
  headers: {
    "X-Custom-Header": "value",
  },
};

Accessing Express Features

Since Transit-Kit is built on Express, you can access the underlying Express app:

const server = createServer({ /* ... */ });

// Add custom middleware
server.expressApp.use(cors());
server.expressApp.use(helmet());

// Static files
server.expressApp.use(express.static("public"));

// Custom routes
server.expressApp.get("/health", (req, res) => {
  res.json({ status: "ok" });
});

Project Structure

Transit-Kit exports two main modules:

  • transit-kit/server — Server creation, endpoint handlers, and authentication
  • transit-kit/generator — OpenAPI documentation generation

TypeScript Configuration

For the best experience, ensure your tsconfig.json includes:

{
  "compilerOptions": {
    "strict": true,
    "esModuleInterop": true,
    "moduleResolution": "node",
    "target": "ES2022",
    "module": "ES2022"
  }
}

License

MIT © D4rkr34lm

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Links