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 🙏

© 2025 – Pkg Stats / Ryan Hefner

ts-typed-api

v0.2.16

Published

A lightweight, type-safe RPC library for TypeScript with Zod validation

Readme

ts-typed-api 🚀

A lightweight, type-safe API library for TypeScript with Zod validation.

Motivation

After building several full-stack applications, I discovered that Large Language Models (LLMs) face significant challenges when implementing features that span both backend and frontend components, particularly around API interfaces.

The core issues I observed:

  • API Contract Drift: LLMs struggle to maintain consistency when defining an API endpoint and then implementing its usage in the frontend
  • Context Loss: Without a clear, shared contract, LLMs lack the contextual assistance needed to ensure proper integration between client and server
  • Integration Errors: The disconnect between backend definitions and frontend consumption leads to runtime errors that could be prevented

The Solution: Leverage TypeScript's powerful type system to provide real-time feedback and compile-time validation for both LLMs and developers. By creating a shared contract that enforces consistency across the entire stack, we eliminate the guesswork and reduce integration issues.

🤖 Built for LLM-Assisted Development

This module is specifically designed to make coding with Large Language Models (LLMs) easier and more efficient. When working on bigger applications with extensive APIs, maintaining context becomes challenging for both developers and AI assistants. ts-typed-api solves this by:

🔑 Key Benefits for LLM Development

  • Centralized Type Definitions: Keep all API contracts in one place, making it easier for LLMs to understand your entire API surface
  • Automatic Type Synchronization: The type system ensures both client and server stay perfectly in sync, preventing the drift that commonly occurs in large codebases. Compile-time checks prevent the common client-server mismatches that occur in AI-assisted development
  • Context-Friendly Structure: Organized domain-based API definitions that LLMs can easily parse and understand
  • Compile-Time Validation: Catch integration issues before runtime, reducing the debugging cycles when working with AI-generated code
  • Self-Documenting: Type definitions serve as living documentation that LLMs can easily parse

📦 Installation

npm install --save ts-typed-api

How to use it?

  1. Define your API in a file that will be shared by both the server and the client
  2. Implement handlers in the server, leveraging the type system and request/response validation
  3. Implement the client based on the contract from #1 leveraging type system

Examples

Check out the examples/ directory:

  • simple/ - Basic usage with ping endpoints and middleware
  • advanced/ - Complex schemas with authentication, CRUD operations, and file uploads

🚀 Quick Start

1. Define API Routes with Domains

Create your API definitions organized by logical domains:

// definitions.ts
import { ZodSchema as z, CreateApiDefinition, CreateResponses } from 'ts-typed-api';

// you can create multiple definitions per app
export const PublicApiDefinition = CreateApiDefinition({
    prefix: '/api/v1/public',
    endpoints: {
        common: { // domain name
            ping: { // endpoint name
                method: 'GET',
                path: '/ping',
                // validate route parameters with Zod
                params: z.object({}),
                // validate query parameters with Zod
                query: z.object({}), 
                // validate body with Zod
                body: z.object({}),
                // validate query parameters with Zod
                responses: CreateResponses({
                    // specify response codes and response shapes
                    200: z.enum(["pong"]),
                    201: z.boolean()
                })
            },
        }
    }
});

2. Implement Server-Side Handlers

Register handlers with full type safety and middleware support:

// server.ts
import express from 'express';
import { RegisterHandlers, EndpointMiddleware } from 'ts-typed-api';
import { PublicApiDefinition } from './definitions';

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

// Example middleware with endpoint information
const loggingMiddleware: EndpointMiddleware = (req, res, next, endpointInfo) => {
    console.log(`[${new Date().toISOString()}] ${req.method} ${req.path} - Endpoint: ${endpointInfo.domain}.${endpointInfo.routeKey}`);
    next();
};

// Register handlers with TypeScript enforcing all required handlers are present
RegisterHandlers(app, PublicApiDefinition, {
    common: {
        ping: async (req, res) => {
            console.log('Ping endpoint called');
            res.respond(200, "pong");
        }
    },
}, [loggingMiddleware]);

app.listen(3001, () => {
    console.log('Server running on http://localhost:3001');
});

3. Type-Safe Client Calls

Make API calls with full type safety and response handling:

// client.ts
import { ApiClient, FetchHttpClientAdapter } from 'ts-typed-api';
import { PublicApiDefinition } from './definitions';

async function runClientExample(): Promise<void> {
    const apiClient = new ApiClient('http://localhost:3001', PublicApiDefinition);

    // Type-safe API calls with response handlers
    await apiClient.callApi('common', 'ping', {}, {
        // you have to handle each possible status code defined in your contract
        200: (payload) => {
            console.log('Success:', payload); // payload is typed as "pong"
        },
        201: (payload) => {
            console.log('Success:', payload); // payload is typed as boolean
        },
        // you always need to handle 422 since it's returned on request validation
        422: (payload) => {
            console.log('Request validation error:', payload);
        }
    });
}

Now both server and client are type safe and in sync! The moment you change the definition of the API, type system will let you know about potential changes you need to handle (like additional response code or a change request body schema).

4. File Upload Example

Handle file uploads with type-safe validation:

// Define file upload endpoints
const FileUploadApiDefinition = CreateApiDefinition({
    prefix: '/api',
    endpoints: {
        files: {
            uploadSingle: {
                path: '/upload/single',
                method: 'POST',
                body: z.object({
                    description: z.string().optional(),
                }),
                fileUpload: {
                    single: {
                        fieldName: 'file',
                        maxSize: 5 * 1024 * 1024, // 5MB
                        allowedMimeTypes: ['image/jpeg', 'image/png', 'image/gif']
                    }
                },
                responses: CreateResponses({
                    200: z.object({
                        message: z.string(),
                        fileInfo: z.object({
                            originalName: z.string(),
                            size: z.number(),
                            mimetype: z.string()
                        })
                    })
                })
            }
        }
    }
});

// Implement handler
RegisterHandlers(app, FileUploadApiDefinition, {
    files: {
        uploadSingle: async (req, res) => {
            const file = req.file as UploadedFile | undefined;
            
            res.respond(200, {
                message: 'File uploaded successfully',
                fileInfo: {
                    originalName: file!.originalname,
                    size: file!.size,
                    mimetype: file!.mimetype
                }
            });
        }
    }
});

🌟 Features

Custom HTTP Client Adapters

Create custom HTTP client adapters by implementing the HttpClientAdapter interface:

interface HttpClientAdapter {
  request<T = any>(url: string, options: HttpRequestOptions): Promise<HttpResponse<T>>;
}

Middleware System

Add cross-cutting concerns like authentication, logging, and validation:

const customMiddleware: EndpointMiddleware = (req, res, next, endpointInfo) => {
    // Access to endpoint metadata
    console.log(`Domain: ${endpointInfo.domain}, Route: ${endpointInfo.routeKey}`);
    next();
};

Roadmap

  • OpenAPI generation with dynamic documentation based on Swagger

🤝 Contributing

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

📄 License

Apache 2.0 License