npm package: @honorest/honorest-contract
Description: Contract integration for HonorestJS - Enables contract-first API development with automatic validation
Author: ctrhub
Latest version: 0.1.2

# @honorest/honorest-contract

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

[![npm version](https://img.shields.io/npm/v/@honorest/honorest-contract.svg)](https://www.npmjs.com/package/@honorest/honorest-contract)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

`@honorest/honorest-contract` bridges [@honorest/contract](https://github.com/honorestjs/contract) with the [HonorestJS](https://github.com/honorestjs/honorest) 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](https://github.com/honorestjs/contract)  
🚀 **Zero Boilerplate** - Just add the decorator, validation works automatically  
⚡ **Built-in Interceptor** - Leverages HonorestJS's interceptor system  

## Installation

```bash
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`:

```typescript
// 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:

```typescript
// 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.

```typescript
@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:**

```typescript
@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.

```typescript
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).

```typescript
// 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):
```json
{
  "message": "Request validation failed",
  "cause": [
    { "field": "params", "message": "Invalid path parameters", "code": "INVALID_PARAMS" }
  ]
}
```

**Response Validation Failure** (500 Internal Server Error):
```json
{
  "message": "Internal server error: Invalid response format"
}
```

**Invalid Request Format** (400 Bad Request):
```json
{
  "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:

```typescript
// ❌ 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:

```typescript
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](CONTRIBUTING.md) for details.

## License

MIT © [HonorestJS](https://github.com/honorestjs)

## Related Packages

- [`honorestjs`](https://github.com/honorestjs/honorest) - HonorestJS server framework
- [`@honorest/contract`](https://github.com/honorestjs/contract) - Framework-agnostic contract definitions
- [`@honorest/client`](https://github.com/honorestjs/client) - Type-safe HTTP client SDK