# How Instatic Uses TypeBox for Boundary Validation: A Complete Guide

> Learn how Instatic uses TypeBox for robust boundary validation with a validate-then-trust workflow. Explore parsing helpers and compiled validators for secure input handling.

- Repository: [CoreBunch/Instatic](https://github.com/CoreBunch/Instatic)
- Tags: how-to-guide
- Published: 2026-07-31

---

**Instatic validates every untrusted input crossing system boundaries by passing it through TypeBox schemas using a "validate-then-trust" workflow with helpers like `parseValue`, `safeParseValue`, and compiled validators for hot paths.**

In the CoreBunch/Instatic codebase, **TypeBox boundary validation** serves as the single source of truth for all data integrity. Whether handling HTTP requests, persisted JSON, or plugin manifests, the architecture enforces strict runtime checks that eliminate parallel TypeScript interfaces and ensure type safety across the entire stack.

## The "Validate-Then-Trust" Architecture

Instatic treats every external datum as a potential threat until proven otherwise. The validation workflow follows three strict steps: define a schema using `@sinclair/typebox`, parse the incoming value with specialized helpers, and cache compiled validators for performance-critical paths. This pattern appears consistently across HTTP handlers, storage layers, and plugin systems according to the repository's architectural tests in [`src/__tests__/architecture/boundary-validation.test.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/__tests__/architecture/boundary-validation.test.ts).

## Core Validation Helpers in [`typeboxHelpers.ts`](https://github.com/CoreBunch/Instatic/blob/main/typeboxHelpers.ts)

The primary validation API lives in [`src/core/utils/typeboxHelpers.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/utils/typeboxHelpers.ts). This module exports the `Type` builder, `Static` type extractor, and four critical boundary functions that handle different failure modes.

### Hard Boundaries with `parseValue`

For critical boundaries where invalid data represents an unrecoverable error, Instatic uses `parseValue(schema, value)`. This function performs strict parsing and throws immediately on validation failure, making it ideal for HTTP request bodies and configuration files.

```typescript
import { Type, parseValue } from '@core/utils/typeboxHelpers';

const CreateUserSchema = Type.Object({
  email: Type.String({ format: 'email' }),
  password: Type.String({ minLength: 8 })
});

// Throws if validation fails
const user = parseValue(CreateUserSchema, rawBody);

```

### Soft Boundaries with `safeParseValue`

When callers need to handle validation failures gracefully, `safeParseValue(schema, value)` returns a discriminated union: `{ ok: true; value } | { ok: false; errors }`. This pattern appears in user preferences and optional metadata parsing where partial failure is acceptable.

### Fallback Handling and Array Filtering

The `withFallback(schema, fallback)` helper annotates schemas with default values for tolerant parsers, while `filterArray(itemSchema, values)` keeps only entries that satisfy a schema—useful for cleaning imported data without failing the entire operation. The module also exports `formatValueErrors` for human-readable error messages.

## Performance Optimization with Compiled Validators

To avoid the overhead of `Value.Check` on every call, [`src/core/utils/typeboxCompiler.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/utils/typeboxCompiler.ts) implements a caching layer. Functions like `compiled(schema)`, `compiledCheck`, `compiledSafeParseValue`, and `compiledDecode` compile a TypeBox validator once per schema object and reuse it across subsequent validations.

```typescript
import { compiledCheck, compiledDecode } from '@core/utils/typeboxCompiler';
import { DataRowSchema } from '@/modules/data/rowSchema';

if (!compiledCheck(DataRowSchema, rawRow)) {
  throw new Error('Invalid data row');
}
const row = compiledDecode(DataRowSchema, rawRow);

```

## Boundary-Specific Implementation Patterns

Different system boundaries require specialized validation strategies. Instatic centralizes these patterns in dedicated modules to ensure consistent **TypeBox boundary validation** across the application.

### HTTP Request Bodies in [`server/http.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/http.ts)

Server-side handlers validate incoming payloads using `readValidatedBody` in [`server/http.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/http.ts). This utility extracts the raw body, enforces size limits, runs `safeParseValue` against the provided schema, and returns either the typed value or `null`—triggering a `badRequest` response on failure.

```typescript
import { readValidatedBody, badRequest } from '../../http';

const body = await readValidatedBody(req, CreateUserSchema);
if (!body) return badRequest('Invalid request body');
// body is now typed as { email: string; password: string }

```

### Client-Side API Calls in [`apiClient.ts`](https://github.com/CoreBunch/Instatic/blob/main/apiClient.ts)

The client HTTP layer ([`src/core/http/apiClient.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/http/apiClient.ts)) automatically validates responses through `apiRequest` and `apiBlobRequest`. These helpers include credentials, execute the fetch, and validate the JSON payload against a TypeBox schema, throwing a unified `ApiError` on any mismatch.

```typescript
import { apiRequest } from '@core/http';
import { Type } from '@core/utils/typeboxHelpers';

const PostsResponseSchema = Type.Object({
  rows: Type.Array(Type.Object({
    id: Type.String(),
    title: Type.String()
  }))
});

const data = await apiRequest('/admin/api/cms/posts', {
  schema: PostsResponseSchema
});

```

### Persisted JSON and Plugin Manifests

For data stored in localStorage, database JSON columns, or plugin manifests, [`src/core/utils/jsonValidate.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/utils/jsonValidate.ts) provides `safeParseJson`, `parseJsonWithFallback`, and `parseJsonResponse`. The persistence layer ([`src/core/persistence/validate.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/persistence/validate.ts)) and plugin system ([`src/core/plugins/manifest.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/plugins/manifest.ts)) reuse the same TypeBox schemas, ensuring validation consistency across storage boundaries.

```typescript
import { parseJsonWithFallback } from '@core/utils/jsonValidate';
import { EditorPreferencesSchema } from '@/modules/editor/preferences';

const prefs = parseJsonWithFallback(
  localStorage.getItem('editorPrefs') ?? '',
  EditorPreferencesSchema,
  DEFAULT_PREFERENCES
);

```

## Source of Truth: Schemas Over Interfaces

Instatic enforces a strict architectural rule: the TypeBox schema is the authoritative definition. Developers derive TypeScript types using `Static<typeof Schema>` (re-exported in [`typeboxHelpers.ts`](https://github.com/CoreBunch/Instatic/blob/main/typeboxHelpers.ts)), eliminating duplicate `interface` declarations. Architectural tests verify that every boundary validation follows this pattern, ensuring no untyped data enters the system.

## Summary

- **TypeBox boundary validation** in Instatic follows a "validate-then-trust" principle for all external data crossing system boundaries.
- [`src/core/utils/typeboxHelpers.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/utils/typeboxHelpers.ts) provides `parseValue` for hard boundaries and `safeParseValue` for soft boundaries, plus `withFallback` and `filterArray` for tolerant parsing.
- [`src/core/utils/typeboxCompiler.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/utils/typeboxCompiler.ts) caches compiled validators via `compiledCheck` and `compiledDecode` to optimize hot-path performance.
- Server-side request validation uses `readValidatedBody` in [`server/http.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/http.ts), while client calls use `apiRequest` from [`src/core/http/apiClient.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/http/apiClient.ts).
- Persisted JSON and plugin manifests validate through [`src/core/utils/jsonValidate.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/utils/jsonValidate.ts), sharing schemas with [`src/core/persistence/validate.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/persistence/validate.ts) and [`src/core/plugins/manifest.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/plugins/manifest.ts).
- All TypeScript types derive from schemas via `Static<typeof Schema>`, with architectural tests in [`src/__tests__/architecture/boundary-validation.test.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/__tests__/architecture/boundary-validation.test.ts) enforcing compliance.

## Frequently Asked Questions

### What is the difference between `parseValue` and `safeParseValue` in Instatic?

`parseValue` performs strict validation and throws immediately on failure, making it suitable for hard boundaries like HTTP request bodies where invalid data is an unrecoverable error. `safeParseValue` returns a discriminated union `{ ok: true; value } | { ok: false; errors }` that allows the caller to handle validation failures gracefully, ideal for soft boundaries such as user preferences or optional configuration.

### How does Instatic optimize TypeBox validation performance for high-frequency operations?

Instatic caches compiled validators in [`src/core/utils/typeboxCompiler.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/utils/typeboxCompiler.ts) using functions like `compiled`, `compiledCheck`, `compiledSafeParseValue`, and `compiledDecode`. These utilities compile the TypeBox schema once per unique schema object and reuse the validator across subsequent calls, avoiding the runtime overhead of repeated `Value.Check` operations.

### Where does Instatic validate HTTP request bodies and API responses?

Server-side request bodies are validated in [`server/http.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/http.ts) using the `readValidatedBody` helper, which returns `null` for invalid payloads and enforces size limits. Client-side responses are validated automatically by `apiRequest` and `apiBlobRequest` in [`src/core/http/apiClient.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/http/apiClient.ts), which throw `ApiError` when response data fails schema validation.

### How does Instatic ensure TypeBox schemas remain the single source of truth?

The codebase derives all TypeScript types from TypeBox schemas using `Static<typeof Schema>`, explicitly avoiding parallel `interface` declarations. Architectural tests in [`src/__tests__/architecture/boundary-validation.test.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/__tests__/architecture/boundary-validation.test.ts) enforce this rule, ensuring every boundary validation references a TypeBox schema rather than a standalone type definition.