How Instatic Validates Data at Every Boundary Using TypeBox
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, 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, 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.
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 exports readValidatedBody, which wraps the raw request parsing and validation into a single operation. This guarantees that route handlers never encounter unvalidated request bodies.
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 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:
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:
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:
const data = await parseJsonResponse(response, ApiResponseSchema);
Compiled Validator Optimization
To ensure runtime performance without recompilation overhead, 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.
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 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:
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
apiRequestclient insrc/core/http/apiClient.tsvalidates HTTP responses automatically. - Server-side handlers use
readValidatedBodyfromserver/http.tsto ensure request bodies match expected schemas. - JSON utilities in
src/core/utils/jsonValidate.tsprovide safe parsing with fallback options for local storage and discriminated unions for persistence. - Compiled validators in
src/core/utils/typeboxCompiler.tsoptimize performance by caching compiled schema checks. - Five boundary rules documented in
docs/reference/typebox-patterns.mdcover 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 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 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. This document serves as the authoritative reference for developers implementing new boundary crossings in the codebase.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →