# How Instatic Validates Data at Every Boundary Using TypeBox

> Instatic validates data at every boundary using TypeBox for strict security. Learn how Instatic enforces a validate-then-trust contract across HTTP, JSON.parse, plugins, and persistence.

- Repository: [CoreBunch/Instatic](https://github.com/CoreBunch/Instatic)
- Tags: deep-dive
- Published: 2026-07-03

---

**Instatic enforces a strict "validate-then-trust" contract by passing all untyped data through TypeBox schemas at HTTP, JSON.parse, plugin manifest, and persistence boundaries.**

Instatic implements a zero-trust architecture for data integrity by requiring TypeBox validation at every system boundary. Whether data enters via HTTP requests, `JSON.parse` operations, or plugin manifests, the codebase refuses to process unchecked payloads. This approach ensures that internal functions operate only on guaranteed shapes, eliminating runtime surprises from malformed inputs.

## The Validate-Then-Trust Architecture

According to the design documentation in [`docs/reference/typebox-patterns.md`](https://github.com/CoreBunch/Instatic/blob/main/docs/reference/typebox-patterns.md), Instatic defines five critical boundary rules covering HTTP traffic, JSON parsing, raw fetch responses, direct `req.json()` calls, and field casting. The pattern is simple: any data crossing from an untyped external context into the typed internal system must first satisfy a TypeBox schema.

## HTTP Client Validation in apiRequest

In [`src/core/http/apiClient.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/http/apiClient.ts), the `apiRequest` function serves as the exclusive gateway for browser-to-server communication. This helper automatically validates HTTP response payloads against a supplied TypeBox schema before returning control to the caller.

```typescript
import { apiRequest } from './src/core/http/apiClient';
import { UsersResponseSchema } from './schemas';

const users = await apiRequest('/admin/api/cms/users', {
  schema: UsersResponseSchema
});

```

If the response body fails schema validation, `apiRequest` throws a typed `ApiError`, preventing corrupt data from reaching React state or downstream business logic.

## Server-Side Request Parsing with readValidatedBody

On the server side, [`server/http.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/http.ts) exports `readValidatedBody`, which wraps the raw request parsing and validation into a single operation. This guarantees that route handlers never encounter unvalidated request bodies.

```typescript
import { readValidatedBody } from './server/http';
import { CreateUserSchema } from './schemas';

export async function POST(req: Request) {
  const body = await readValidatedBody(req, CreateUserSchema);
  // body is now guaranteed to match CreateUserSchema
}

```

## JSON Validation Utilities

The [`src/core/utils/jsonValidate.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/utils/jsonValidate.ts) module provides low-level building blocks for safe JSON parsing with schema enforcement. These utilities handle discriminated unions for error handling versus soft-fallback scenarios.

### safeParseJson for Discriminated Results

**safeParseJson** returns a discriminated union distinguishing between successful validation and parsing failures:

```typescript
import { safeParseJson } from './src/core/utils/jsonValidate';
import { SiteSchema } from './schemas';

const result = safeParseJson(rawSiteJson, SiteSchema);
if (result.success) {
  // result.data is typed SiteSchema
} else {
  // handle result.error
}

```

### parseJsonWithFallback for Optional Config

**parseJsonWithFallback** offers best-effort parsing for optional configuration or local storage, silently falling back to safe defaults when data is corrupted:

```typescript
const settings = parseJsonWithFallback(
  localStorage.getItem('settings'),
  SettingsSchema,
  defaultSettings
);

```

### parseJsonResponse for Fetch Handling

**parseJsonResponse** handles fetch `Response` objects, validating the JSON payload against a schema and throwing on mismatch:

```typescript
const data = await parseJsonResponse(response, ApiResponseSchema);

```

## Compiled Validator Optimization

To ensure runtime performance without recompilation overhead, [`src/core/utils/typeboxCompiler.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/utils/typeboxCompiler.ts) implements a compile-once strategy. Each TypeBox schema is compiled to a reusable validator function that gets cached and reused across subsequent validation calls.

```typescript
import { compileSchema } from './src/core/utils/typeboxCompiler';

const validator = compileSchema(MySchema);
// Reuse validator for multiple checks without recompilation
const result = validator.check(data);

```

This optimization is critical for high-frequency boundaries like HTTP APIs and persistent storage reads.

## Boundary-Specific Validation Patterns

### Plugin Manifest Validation

Plugin manifests undergo strict validation before runtime instantiation via `parsePluginManifest` in the plugin SDK, which internally relies on the same TypeBox infrastructure to ensure manifest structure compliance.

### Persistence Layer Validation

When loading documents from database JSON columns, [`src/core/persistence/validate.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/persistence/validate.ts) uses `safeParseJson` to ensure stored site documents conform to canonical schemas before entering the editor context.

### Internal Server-to-Server Communication

For internal fetch operations, the server-side `readEnvelope` function validates responses against schemas without requiring a secondary wrapper, maintaining the same strict guarantees across service boundaries:

```typescript
const res = await fetch(internalEndpoint);
const data = await readEnvelope(res, SomeSchema);

```

## Summary

- Instatic validates all data at system boundaries using TypeBox schemas before processing internally.
- The `apiRequest` client in [`src/core/http/apiClient.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/http/apiClient.ts) validates HTTP responses automatically.
- Server-side handlers use `readValidatedBody` from [`server/http.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/http.ts) to ensure request bodies match expected schemas.
- JSON utilities in [`src/core/utils/jsonValidate.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/utils/jsonValidate.ts) provide safe parsing with fallback options for local storage and discriminated unions for persistence.
- Compiled validators in [`src/core/utils/typeboxCompiler.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/utils/typeboxCompiler.ts) optimize performance by caching compiled schema checks.
- Five boundary rules documented in [`docs/reference/typebox-patterns.md`](https://github.com/CoreBunch/Instatic/blob/main/docs/reference/typebox-patterns.md) cover HTTP, JSON.parse, raw fetch, req.json, and field casting.

## Frequently Asked Questions

### What happens when TypeBox validation fails in apiRequest?

When validation fails in `apiRequest`, the function throws a typed `ApiError` containing detailed information about the schema mismatch. This prevents the invalid payload from ever reaching application state and allows error boundaries to catch and handle validation failures appropriately.

### How does Instatic handle corrupted JSON in localStorage?

Instatic uses `parseJsonWithFallback` from [`src/core/utils/jsonValidate.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/utils/jsonValidate.ts) to handle potentially corrupted localStorage entries. This utility attempts to parse and validate the stored JSON against a TypeBox schema, returning a safe default value if parsing fails or the data doesn't match the schema, ensuring the UI never crashes due to malformed persisted state.

### Why does Instatic compile TypeBox schemas instead of using them directly?

The repository compiles schemas once via [`src/core/utils/typeboxCompiler.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/utils/typeboxCompiler.ts) to avoid the performance cost of recompiling validation logic on every function call. This compile-once pattern significantly improves runtime performance for high-frequency operations like API requests and database reads while maintaining strict type safety.

### Where are the boundary validation rules documented?

The five specific boundary rules—covering HTTP traffic, JSON.parse operations, raw fetch responses, direct req.json consumption, and field casting—are documented in [`docs/reference/typebox-patterns.md`](https://github.com/CoreBunch/Instatic/blob/main/docs/reference/typebox-patterns.md). This document serves as the authoritative reference for developers implementing new boundary crossings in the codebase.