Schema Validation Hook

Schema Validation Hook

The schema validation hook is a SvelteKit handle hook that validates incoming requests and optionally outgoing responses against your schemas.

Basic Setup

Add the hook to src/hooks.server.ts:

import { sequence } from "@sveltejs/kit/hooks";
import { createSchemaValidationHook } from "sveltekit-auto-openapi/schema-validation-hook";

export const handle = sequence(
  createSchemaValidationHook({
    validateOutput: import.meta.env.DEV,
  })
);
ts

Configuration Options

validateOutput

Controls whether response validation is enabled.

createSchemaValidationHook({
  validateOutput: import.meta.env.DEV, // Only validate in development
})
ts

Recommended:

  • true in development - Catch response validation errors early
  • false in production - Avoid performance overhead

How It Works

The validation hook intercepts every request:

  1. Route Matching - Converts SvelteKit route ID to OpenAPI path format
  2. Config Lookup - Loads validation configuration from virtual module
  3. Input Validation - Validates headers, cookies, query params, and body
  4. Handler Execution - Calls your route handler if validation passes
  5. Output Validation - Validates response (if enabled)

Validation Flow

Request

  ├─> Has validation config?
  │     │
  │     ├─> No  ──> Pass through to handler
  │     │
  │     └─> Yes ──> Validate input
  │                   │
  │                   ├─> Validation fails ──> Return 400 Error
  │                   │
  │                   └─> Validation passes ──> Call handler
  │                                                │
  │                                                └─> Response
  │                                                      │
  │                                                      ├─> validateOutput disabled ──> Return response
  │                                                      │
  │                                                      └─> validateOutput enabled
  │                                                            │
  │                                                            ├─> Output validation fails ──> Return 500 Error
  │                                                            │
  │                                                            └─> Output validation passes ──> Return response
null

Validated Properties

Request (Input)

  • Headers - via $headers in _config
  • Cookies - via $cookies in _config
  • Query Parameters - via $query in _config
  • Path Parameters - via $pathParams in _config
  • Request Body - via requestBody in _config

Response (Output)

  • Headers - via responses[statusCode].headers
  • Cookies - via response Set-Cookie validation
  • Response Body - via responses[statusCode].content

Error Handling

Validation Errors (400 Bad Request)

When input validation fails:

{
  "error": "Validation failed",
  "details": [
    {
      "path": "/email",
      "message": "must be a valid email",
      "keyword": "format"
    }
  ]
}
js

Error detail visibility:

  • Development: Detailed errors with $showErrorMessage: true
  • Production: Generic errors for security

Response Validation Errors (500 Internal Server Error)

When output validation fails (only if validateOutput: true):

{
  "error": "Response validation failed",
  "details": [...]
}
js

Performance Considerations

Input Validation

  • Minimal overhead (~1-2ms per request)
  • Uses fast @cfworker/json-schema validator
  • Only validates routes with _config exports

Output Validation

  • Additional overhead (~1-2ms per response)
  • Recommended for development only
  • Disable in production for better performance

Validation Registry

The hook uses the validation registry from the virtual module:

{
  "/api/users/{id}": {
    POST: {
      hasInput: true,
      hasOutput: true,
      input: { /* validation schemas */ },
      output: { /* validation schemas */ }
    }
  }
}
ts

Special Behaviors

Skip Validation

Use $skipValidation: true to disable validation for specific schemas:

$headers: {
  $skipValidation: true,
  schema: z.object({ ... })
}
ts

Show Error Messages

Control error message detail with $showErrorMessage:

requestBody: {
  content: {
    "application/json": {
      $showErrorMessage: import.meta.env.DEV, // Detailed errors in dev only
      schema: z.object({ ... })
    }
  }
}
ts

Unimplemented Handlers

If a route has _config but no handler function, returns:

501 Not Implemented
null

Next Steps