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

@honorestjs/contract

v0.1.1

Published

Contract-first API definitions for HonorestJS - Framework-agnostic contract definitions with full type safety

Readme

@honorestjs/contract

Framework-agnostic contract definitions for type-safe APIs

npm version License: MIT

@honorestjs/contract provides a powerful, type-safe way to define API contracts that can be shared between servers and clients. Built on top of Zod, it enables contract-first development with full TypeScript type inference and runtime validation.

Features

Contract-First Development - Define your API before implementation
🔒 Full Type Safety - Complete TypeScript type inference from contracts
Runtime Validation - Built-in validation using Zod schemas
🎯 Framework Agnostic - Use with any server or client framework
📝 OpenAPI Compatible - Generate OpenAPI specs from contracts
🚀 Zero Code Generation - Pure TypeScript, no build steps required

Installation

npm install @honorestjs/contract zod
# or
yarn add @honorestjs/contract zod
# or
pnpm add @honorestjs/contract zod
# or
bun add @honorestjs/contract zod

Quick Start

1. Define Your Contract

import { defineContract, endpoint } from '@honorestjs/contract'
import { z } from 'zod'

// Define schemas
const UserSchema = z.object({
  id: z.string().uuid(),
  name: z.string(),
  email: z.string().email()
})

const CreateUserSchema = z.object({
  name: z.string().min(1),
  email: z.string().email()
})

// Define contract
export const UsersContract = defineContract({
  name: 'users',
  path: '/users',
  description: 'User management endpoints',
  endpoints: {
    getUser: endpoint({
      method: 'GET',
      path: '/:id',
      params: z.object({ id: z.string().uuid() }),
      output: UserSchema,
      description: 'Get a user by ID',
      errors: {
        404: z.object({ message: z.string() })
      }
    }),
    
    listUsers: endpoint({
      method: 'GET',
      path: '/',
      query: z.object({
        page: z.number().int().positive().default(1),
        limit: z.number().int().positive().max(100).default(20)
      }),
      output: z.object({
        users: z.array(UserSchema),
        total: z.number()
      })
    }),
    
    createUser: endpoint({
      method: 'POST',
      path: '/',
      body: CreateUserSchema,
      output: UserSchema,
      errors: {
        400: z.object({ message: z.string(), errors: z.array(z.any()) })
      }
    })
  }
})

2. Create an App Contract

import { defineApp } from '@honorestjs/contract'
import { UsersContract } from './contracts/users'
import { PostsContract } from './contracts/posts'

export const AppContract = defineApp({
  version: 1,
  prefix: '/api',
  contracts: {
    users: UsersContract,
    posts: PostsContract
  },
  metadata: {
    title: 'My API',
    version: '1.0.0',
    description: 'RESTful API for my application'
  }
})

3. Use Type Inference

import type { ContractInput, ContractOutput } from '@honorestjs/contract'

// Extract input types
type GetUserInput = ContractInput<typeof UsersContract, 'getUser'>
// { params: { id: string }, query: never, body: never, headers: never }

// Extract output types
type GetUserOutput = ContractOutput<typeof UsersContract, 'getUser'>
// { id: string, name: string, email: string }

// Use in your implementation
function getUser(input: GetUserInput['params']): Promise<GetUserOutput> {
  // Implementation
}

API Reference

Builders

endpoint(definition)

Creates an endpoint definition.

const getUserEndpoint = endpoint({
  method: 'GET',                              // HTTP method
  path: '/:id',                               // Endpoint path
  params: z.object({ id: z.string() }),      // Path parameters schema
  query: z.object({ include: z.string() }),  // Query parameters schema (optional)
  body: z.object({ ... }),                    // Request body schema (optional)
  headers: z.object({ ... }),                 // Headers schema (optional)
  output: z.object({ ... }),                  // Response schema
  errors: {                                   // Error responses (optional)
    404: z.object({ message: z.string() })
  },
  description: 'Get a user by ID',            // Description (optional)
  summary: 'Get user',                        // Summary (optional)
  tags: ['users'],                            // Tags (optional)
  deprecated: false                           // Deprecated flag (optional)
})

defineContract(definition)

Creates a contract (collection of endpoints).

const UsersContract = defineContract({
  name: 'users',                    // Contract name
  path: '/users',                   // Base path
  version: 1,                       // API version (optional)
  description: 'User endpoints',    // Description (optional)
  tags: ['users'],                  // Tags (optional)
  endpoints: {                      // Endpoint definitions
    getUser: getUserEndpoint,
    createUser: createUserEndpoint
  }
})

defineApp(definition)

Creates an app (collection of contracts).

const AppContract = defineApp({
  version: 1,                       // Global API version (optional)
  prefix: '/api',                   // Global prefix (optional)
  contracts: {                      // Contract definitions
    users: UsersContract,
    posts: PostsContract
  },
  metadata: {                       // App metadata (optional)
    title: 'My API',
    version: '1.0.0',
    description: 'API description',
    contact: { name: 'Support', email: '[email protected]' },
    license: { name: 'MIT' }
  }
})

Type Utilities

ContractInput<TContract, TEndpoint>

Extracts input type from a contract endpoint.

type Input = ContractInput<typeof UsersContract, 'getUser'>
// { params: {...}, query: {...}, body: {...}, headers: {...} }

ContractOutput<TContract, TEndpoint>

Extracts output type from a contract endpoint.

type Output = ContractOutput<typeof UsersContract, 'getUser'>
// { id: string, name: string, email: string }

ContractErrors<TContract, TEndpoint>

Extracts error types from a contract endpoint.

type Errors = ContractErrors<typeof UsersContract, 'getUser'>
// { 404: { message: string } }

Validation

validate(schema, data)

Validates data against a Zod schema (async).

const result = await validate(UserSchema, userData)
if (result.success) {
  console.log(result.data) // Validated data
} else {
  console.error(result.error) // Validation error
}

validateEndpointInput(endpoint, input)

Validates all inputs for an endpoint.

const validation = await validateEndpointInput(
  UsersContract.endpoints.getUser,
  {
    params: { id: '123' },
    query: { include: 'posts' }
  }
)

if (!validation.isValid) {
  console.error(validation.errors)
}

validateEndpointOutput(endpoint, output)

Validates endpoint output.

const result = await validateEndpointOutput(
  UsersContract.endpoints.getUser,
  responseData
)

Integration

With HonorestJS Server

import { Controller } from 'honorestjs'
import { Contract } from 'honorestjs/contract'
import { UsersContract } from '@myapp/api-contract'

@Controller()
export class UsersController {
  @Contract(UsersContract.endpoints.getUser)
  async getUser(@Param('id') id: string) {
    // Implementation with automatic validation
  }
}

With @honorest/client

import { createClient } from '@honorest/client'
import { AppContract } from '@myapp/api-contract'

const api = createClient(AppContract, {
  baseUrl: 'http://localhost:3000'
})

// Fully type-safe
const user = await api.users.getUser({ params: { id: '123' }})

Best Practices

1. Organize Contracts by Domain

/contracts
  /users
    schemas.ts
    contract.ts
  /posts
    schemas.ts
    contract.ts
  index.ts  # Export AppContract

2. Reuse Schemas

// Define shared schemas
export const UserSchema = z.object({ ... })
export const CreateUserSchema = UserSchema.omit({ id: true })
export const UpdateUserSchema = CreateUserSchema.partial()

3. Document Everything

const endpoint = endpoint({
  // ...
  description: 'Detailed description of what this endpoint does',
  summary: 'Short summary',
  tags: ['users', 'admin'],
  examples: {
    request: { id: '123' },
    response: { id: '123', name: 'John' }
  }
})

4. Version Your Contracts

const UsersContractV1 = defineContract({
  name: 'users',
  version: 1,
  // ...
})

const UsersContractV2 = defineContract({
  name: 'users',
  version: 2,
  // ...
})

TypeScript Configuration

For best results, enable strict mode in your tsconfig.json:

{
  "compilerOptions": {
    "strict": true,
    "strictNullChecks": true
  }
}

Contributing

Contributions are welcome! Please read our Contributing Guide for details.

License

MIT © HonorestJS

Related Packages