How OmniRoute Implements CORS and Zod Validation for Secure API Routing

OmniRoute centralizes Cross-Origin Resource Sharing (CORS) handling through a pipeline-based middleware system and enforces type-safe request validation using Zod schemas across all API endpoints.

OmniRoute is an open-source API routing layer designed to standardize provider interactions while maintaining strict security boundaries. This analysis examines how the diegosouzapw/OmniRoute repository implements CORS and Zod validation to create a consistent, secure request handling pipeline that applies uniformly across every route.

CORS Implementation Architecture

OmniRoute treats CORS as a cross-cutting concern rather than per-route boilerplate, implementing it through a layered middleware approach that intercepts every request and response.

Centralized Header Definitions

All standard CORS headers are defined as constants in src/shared/utils/cors.ts. The CORS_HEADERS object provides a single source of truth for Access-Control-Allow-Methods, Access-Control-Allow-Headers, and other required fields, ensuring consistency across the application.

Dynamic Origin Resolution

The system supports runtime-configurable allowed origins through the corsOrigins setting managed in src/lib/config/runtimeSettings.ts. When configuration changes, the applyCorsOriginsSection helper dynamically loads src/server/cors/origins.ts to parse CSV-formatted origin strings into an allowed-origin set.

The resolveAllowedOrigin function in src/server/cors/origins.ts validates the request's Origin header against this configured set before injecting appropriate response headers.

Pipeline Middleware Integration

Every API route inherits CORS handling automatically through the authorization pipeline in src/server/authz/pipeline.ts. This pipeline imports applyCorsHeaders and invokes it at critical stages: pre-flight handling, success responses, error handling, and rate-limit rejections.

The applyCorsHeaders function accepts three parameters: the response object, the request object, and an optional corsRelaxOrigin boolean. When corsRelaxOrigin is true, the function supports internal tooling scenarios by bypassing strict origin validation while still injecting standard CORS headers.

Zod Validation Patterns

OmniRoute leverages Zod for runtime type checking, ensuring that all incoming requests match expected schemas before business logic executes.

Schema Organization

Central schema collections reside in src/shared/validation/, with domain-specific files including schemas/settings.ts, schemas/provider.ts, and schemas/routing.ts. This organization allows routes to import shared validation logic or define inline schemas for endpoint-specific requirements.

Routes typically define schemas using standard Zod methods like z.object(), z.string(), and z.number(), then apply modifiers such as .optional() or .passthrough() depending on whether the endpoint needs to allow additional unknown properties.

Runtime Validation Flow

Routes validate requests immediately upon entry before executing business logic. For example, the chat completion endpoint in src/app/api/v1/providers/[provider]/chat/completions/route.ts defines its body schema as:

const routeBodySchema = z.object({ 
  model: z.string().optional() 
}).passthrough();

The schema's safeParse method detects malformed payloads immediately. If validation fails, the route returns a 400 Bad Request response before any provider-specific processing occurs, preventing invalid data from reaching downstream services.

Uniform Error Reporting

When Zod validation fails, routes call the shared buildErrorBody utility from the error handling module to generate sanitized JSON error responses. This ensures consistent error formatting—including standardized status codes and error messages—across the entire API surface, regardless of which specific schema triggered the validation exception.

Practical Implementation Examples

To implement CORS in a custom route, import the utilities from the shared modules and apply headers before returning the response:

import { CORS_HEADERS } from "@/shared/utils/cors";
import { applyCorsHeaders } from "@/server/cors/origins";

export async function GET(request: Request) {
  const response = new Response(
    JSON.stringify({ status: "healthy" }), 
    {
      headers: { "Content-Type": "application/json" },
    }
  );
  
  return applyCorsHeaders(response, request);
}

For POST endpoints requiring strict payload validation, use Zod's safeParse pattern with the error builder:

import { z } from "zod";
import { buildErrorBody } from "@/shared/utils/error";

const payloadSchema = z.object({
  name: z.string().min(1),
  age: z.number().int().positive(),
  provider: z.string().optional(),
});

export async function POST(request: Request) {
  const body = await request.json();
  const result = payloadSchema.safeParse(body);
  
  if (!result.success) {
    return new Response(
      buildErrorBody(400, "Invalid payload", result.error),
      { 
        status: 400,
        headers: { "Content-Type": "application/json" }
      }
    );
  }
  
  const validatedData = result.data;
  // Process validated data...
}

Summary

  • Centralized CORS handling: All CORS logic lives in src/server/cors/origins.ts and src/shared/utils/cors.ts, with automatic application through src/server/authz/pipeline.ts ensuring no route requires manual header management.
  • Runtime origin configuration: The corsOrigins setting in src/lib/config/runtimeSettings.ts allows dynamic updates to allowed origins without code changes, parsed by applyCorsOriginsSection.
  • Type-safe validation: Zod schemas in src/shared/validation/ provide compile-time and runtime type checking, with route-specific schemas often using .passthrough() for flexible provider APIs.
  • Consistent error responses: The buildErrorBody utility standardizes validation error formatting, automatically invoked when safeParse detects schema violations.
  • Pipeline integration: CORS headers are injected via applyCorsHeaders at every pipeline stage, including error paths and rate-limit responses, ensuring cross-origin policies persist even during failures.

Frequently Asked Questions

How does OmniRoute handle preflight OPTIONS requests?

OmniRoute handles OPTIONS requests through the standard applyCorsHeaders function called within the authorization pipeline. When a preflight request arrives at src/server/authz/pipeline.ts, the pipeline detects the method and returns an early response with appropriate Access-Control-Allow-Methods and Access-Control-Allow-Headers headers derived from the CORS_HEADERS constant, without executing downstream business logic.

Can I customize CORS origins without restarting the server?

Yes. The corsOrigins setting in src/lib/config/runtimeSettings.ts supports runtime updates. When administrators modify this configuration, the applyCorsOriginsSection function reparses the CSV-formatted origin list in src/server/cors/origins.ts, updating the allowed-origin set immediately. The resolveAllowedOrigin function uses this updated set for all subsequent requests without requiring a server restart.

What happens when Zod validation fails in OmniRoute?

When a schema's safeParse method returns a failure result, the route immediately constructs a 400 Bad Request response using buildErrorBody, passing the Zod error object for detailed but sanitized error messages. This prevents invalid data from reaching provider APIs and ensures clients receive consistent JSON error structures regardless of which endpoint triggered the validation.

Where should I define custom Zod schemas for new routes?

Define shared schemas in src/shared/validation/ following the existing pattern of domain-specific files like schemas/provider.ts. For endpoint-specific validation that won't be reused, define the schema inline within the route file, as demonstrated in src/app/api/v1/providers/[provider]/chat/completions/route.ts. Always import the z object from the Zod package and use safeParse rather than parse to maintain control over error handling.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →