Skip to content

Complex Example ​

A comprehensive example demonstrating advanced patterns and best practices.

Overview ​

This example showcases a production-ready API with:

  • Multi-domain contract organization
  • Authentication middleware
  • Pagination and filtering
  • Database integration patterns
  • Comprehensive error handling
  • OpenAPI documentation

Project Structure ​

complex/
├── contracts/          # Contract definitions by domain
│   ├── users.contract.ts
│   ├── products.contract.ts
│   ├── orders.contract.ts
│   └── index.ts
├── schemas/            # Reusable schemas
│   ├── common.ts
│   ├── users.ts
│   ├── products.ts
│   └── orders.ts
├── handlers/            # Request handlers by domain
│   ├── users.handlers.ts
│   ├── products.handlers.ts
│   └── orders.handlers.ts
├── middleware/         # Custom middleware
│   └── auth.middleware.ts
├── utils/              # Utilities
│   ├── database.ts
│   ├── pagination.ts
│   ├── auth.ts
│   └── docs.ts
└── index.ts            # Main entry point

Domain Organization ​

Contracts by Domain ​

Each domain has its own contract file:

ts
// contracts/users.contract.ts
export const usersContract = createContract({
  getUsers: { /* ... */ },
  getUserById: { /* ... */ },
  createUser: { /* ... */ },
  updateUser: { /* ... */ },
  deleteUser: { /* ... */ },
});

// contracts/index.ts
export const contract = {
  ...usersContract,
  ...productsContract,
  ...ordersContract,
};

Schema Reuse ​

Schemas are defined once and reused:

ts
// schemas/common.ts
export const PaginationQuery = z.object({
  page: z.number().min(1).default(1),
  limit: z.number().min(1).max(100).default(10),
});

export const ErrorResponse = z.object({
  error: z.string(),
  message: z.string(),
});

// Used in contracts
import { PaginationQuery, ErrorResponse } from "./schemas/common";

Authentication Middleware ​

The example includes authentication middleware:

ts
// middleware/auth.middleware.ts
export function withAuth(request: AuthenticatedRequest): void {
  const authHeader = request.headers.get("authorization");
  const userId = extractUserIdFromAuth(authHeader);
  
  if (!userId) {
    return; // Let handler decide
  }
  
  const user = userDb.findById(userId);
  if (user) {
    request.userId = user.id;
    request.userRole = user.role;
  }
}

// Applied to all routes
const router = createRouter({
  contract,
  before: [withAuth],
  handlers: { /* ... */ },
});

Pagination ​

The example includes pagination utilities:

ts
// utils/pagination.ts
export function paginate<T>(
  items: T[],
  page: number,
  limit: number
): PaginatedResponse<T> {
  const start = (page - 1) * limit;
  const end = start + limit;
  
  return {
    data: items.slice(start, end),
    meta: {
      page,
      limit,
      total: items.length,
      totalPages: Math.ceil(items.length / limit),
    },
  };
}

// Used in handlers
const handler = async (request) => {
  const { page, limit } = request.validatedQuery;
  const users = await getAllUsers();
  const paginated = paginate(users, page, limit);
  
  return request.respond({
    status: 200,
    contentType: "application/json",
    body: paginated,
  });
};

Error Handling ​

Comprehensive error handling patterns:

ts
const handler = async (request) => {
  const user = await getUser(request.validatedParams.id);
  
  if (!user) {
    return request.respond({
      status: 404,
      contentType: "application/json",
      body: {
        error: "Not Found",
        message: "User not found",
      },
    });
  }
  
  // Check permissions
  if (request.userRole !== "admin" && request.userId !== user.id) {
    return request.respond({
      status: 403,
      contentType: "application/json",
      body: {
        error: "Forbidden",
        message: "You don't have permission to access this resource",
      },
    });
  }
  
  return request.respond({
    status: 200,
    contentType: "application/json",
    body: user,
  });
};

OpenAPI Integration ​

The example generates and serves OpenAPI documentation:

ts
const openApiSpec = await createOpenApiSpecification(contract, {
  title: "Complex API",
  version: "1.0.0",
  description: readFileSync(join(import.meta.dirname, "description.md"), "utf8"),
  servers: [{ url: "http://localhost:3000", description: "Localhost" }],
  tags: [
    { name: "Users", description: "User management endpoints" },
    { name: "Products", description: "Product management endpoints" },
    { name: "Orders", description: "Order management endpoints" },
  ],
});

// Serve OpenAPI spec and docs
const router = createRouter({
  contract: {
    ...contract,
    getSpec: { /* ... */ },
    getDocs: { /* ... */ },
  },
  handlers: {
    ...handlers,
    getSpec: async (request) => {
      return request.respond({
        status: 200,
        contentType: "application/json",
        body: openApiSpec,
      });
    },
    getDocs: async (request) => {
      return request.respond({
        status: 200,
        contentType: "text/html",
        body: createSpotlightElementsHtml(),
      });
    },
  },
});

Key Patterns ​

1. Domain Separation ​

Each domain (users, products, orders) has:

  • Its own contract file
  • Its own handler file
  • Its own schema file

2. Schema Reuse ​

Common schemas (pagination, errors) are defined once and reused.

3. Middleware Composition ​

Middleware is composed and reused across domains.

4. Type Safety ​

Full type inference from contracts to handlers.

Running the Example ​

bash
cd examples/complex
npm install
npm run dev

The server will start on http://localhost:3000.

API Endpoints ​

  • GET /users - List users with pagination
  • GET /users/:id - Get user by ID
  • POST /users - Create user
  • PATCH /users/:id - Update user
  • DELETE /users/:id - Delete user
  • GET /products - List products
  • GET /orders - List orders
  • GET /openapi.json - OpenAPI specification
  • GET /docs - Interactive API documentation

What to Learn ​

This example demonstrates:

  • Multi-domain organization
  • Authentication patterns
  • Pagination implementation
  • Error handling strategies
  • OpenAPI integration
  • Production-ready patterns

Next Steps ​

Released under the MIT License.