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

@honorest/honorest-contract

v0.1.2

Published

Contract integration for HonorestJS - Enables contract-first API development with automatic validation

Readme

@honorest/honorest-contract

Contract integration for HonorestJS - Enables contract-first API development with automatic validation

npm version License: MIT

@honorest/honorest-contract bridges @honorest/contract with the HonorestJS framework, enabling contract-first API development with automatic runtime validation.

Features

Seamless Integration - Works directly with HonorestJS's decorator system
🔒 Automatic Validation - Validates requests and responses using contract schemas
@Contract() Decorator - Simple decorator to apply contracts to methods
🎯 Type-Safe - Full TypeScript type inference from @honorest/contract
🚀 Zero Boilerplate - Just add the decorator, validation works automatically
Built-in Interceptor - Leverages HonorestJS's interceptor system

Installation

npm install @honorest/honorest-contract @honorest/contract honorestjs hono zod reflect-metadata
# or
yarn add @honorest/honorest-contract @honorest/contract honorestjs hono zod reflect-metadata
# or
pnpm add @honorest/honorest-contract @honorest/contract honorestjs hono zod reflect-metadata
# or
bun add @honorest/honorest-contract @honorest/contract honorestjs hono zod reflect-metadata

Quick Start

1. Define Your Contract

First, create your API contract using @honorest/contract:

// contracts/users.contract.ts
import { defineContract, endpoint } from '@honorest/contract'
import { z } from 'zod'

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

export const UsersContract = defineContract({
  name: 'users',
  path: '/users',
  endpoints: {
    getUser: endpoint({
      method: 'GET',
      path: '/:id',
      params: z.object({ id: z.string().uuid() }),
      output: UserSchema,
      errors: {
        404: z.object({ message: z.string() })
      }
    }),
    
    createUser: endpoint({
      method: 'POST',
      path: '/',
      body: z.object({
        name: z.string().min(1),
        email: z.string().email()
      }),
      output: UserSchema
    })
  }
})

2. Use @Contract() Decorator

Apply the @Contract() decorator to your controller methods. The decorator automatically registers the HTTP method and path from the contract, so you don't need separate HTTP method decorators:

// users.controller.ts
import 'reflect-metadata'
import { Controller, Param, Body } from 'honorestjs'
import { Contract } from '@honorest/honorest-contract'
import { UsersContract } from './contracts/users.contract'
import { UserService } from './user.service'

@Controller()
export class UsersController {
  constructor(private readonly userService: UserService) {}

  @Contract(UsersContract.endpoints.getUser)
  async getUser(@Param('id') id: string) {
    // Request validation happens automatically
    const user = await this.userService.findById(id)
    
    // Response validation happens automatically
    return user
  }

  @Contract(UsersContract.endpoints.createUser)
  async createUser(@Body() data: any) {
    // Request body is automatically validated
    const user = await this.userService.create(data)
    
    // Response is automatically validated
    return user
  }
}

That's it! The @Contract() decorator will automatically:

  • ✅ Register the HTTP route (method + path) from the contract
  • ✅ Validate request params, query, body, and headers
  • ✅ Validate response data before sending to client
  • ✅ Throw proper HTTP exceptions on validation failures
  • ✅ Log validation errors for debugging

API Reference

@Contract() Decorator

Applies a contract endpoint to a controller method, enabling automatic validation and route registration.

@Contract(endpoint: Endpoint)

Parameters:

  • endpoint - An endpoint definition from @honorest/contract

Key Points:

  • The decorator extracts the HTTP method and path from the contract endpoint
  • Do not use @Get(), @Post(), etc. alongside @Contract()
  • The decorator automatically applies the ContractInterceptor for validation
  • Must be used with import 'reflect-metadata' at the top of your file

Example:

@Contract(UsersContract.endpoints.getUser)
async getUser(@Param('id') id: string) {
  return this.userService.findById(id)
}

ContractInterceptor

The interceptor that performs the actual validation. This is automatically applied by the @Contract() decorator.

import { ContractInterceptor } from '@honorest/honorest-contract'

Note: In most cases, you don't need to use this directly. The @Contract() decorator automatically applies it. This export is provided for advanced use cases where you need to apply contract validation to methods decorated with standard HTTP decorators.

ContractMetadataRegistry

Utility class for managing contract metadata (advanced usage).

// Check if a method has a contract
const hasContract = ContractMetadataRegistry.hasContract(
  controllerClass,
  methodName
)

// Get contract for a method
const contract = ContractMetadataRegistry.getContract(
  controllerClass,
  methodName
)

How It Works

The plugin uses a simple but powerful architecture:

  1. @Contract() Decorator - At application bootstrap:

    • Extracts the HTTP method and path from the contract endpoint definition
    • Registers the route with HonorestJS using createHttpHandlerDecorator
    • Stores contract metadata using the Reflector service for type-safe access
    • Automatically applies the ContractInterceptor to the method
  2. ContractInterceptor - During request/response lifecycle:

    • BEFORE handler: Extracts request data (params, query, body, headers) and validates against the contract's input schemas using validateEndpointInput
    • AFTER handler: Validates the handler's return value against the contract's output schema using validateEndpointOutput
    • Throws HTTPException with appropriate status codes on validation failures
    • Logs validation errors to console for debugging
  3. Metadata Management:

    • Uses HonorestJS's Reflector service for type-safe metadata storage
    • Stores contract endpoints on controller method prototypes
    • Manages interceptor metadata to avoid duplicates

Validation Behavior

Request Validation (Input)

  • ✅ Validates params, query, body, and headers if schemas are defined in the contract
  • ✅ Throws HTTPException 400 with error details on validation failure
  • ✅ Only validates fields that have schemas defined

Response Validation (Output)

  • ✅ Validates the handler's return value against the output schema
  • ✅ Throws HTTPException 500 on validation failure (prevents sending invalid data to client)
  • ✅ Logs validation errors to console for debugging

Error Handling

Request Validation Failure (400 Bad Request):

{
  "message": "Request validation failed",
  "cause": [
    { "field": "params", "message": "Invalid path parameters", "code": "INVALID_PARAMS" }
  ]
}

Response Validation Failure (500 Internal Server Error):

{
  "message": "Internal server error: Invalid response format"
}

Invalid Request Format (400 Bad Request):

{
  "message": "Invalid request format",
  "cause": "Error details"
}

Best Practices

1. Share Contracts Across Projects

Keep your contracts in a separate package that both server and client can import:

/packages
  /api-contract          # Shared contracts
    /src
      /users.contract.ts
      /posts.contract.ts
      index.ts
  /api-server            # HonorestJS server
  /web-client            # Frontend application

2. Always Define Output Schemas

Even if you trust your implementation, always define output schemas to catch bugs early:

// ❌ Bad - No output validation
output: z.any()

// ✅ Good - Explicit validation
output: z.object({
  id: z.string().uuid(),
  name: z.string(),
  email: z.string().email()
})

3. Use Error Schemas for Expected Errors

Define schemas for error responses to maintain type safety:

endpoint({
  // ...
  errors: {
    400: z.object({ message: z.string(), errors: z.array(z.any()) }),
    404: z.object({ message: z.string() }),
    500: z.object({ message: z.string() })
  }
})

Contributing

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

License

MIT © HonorestJS

Related Packages