A simple and composable way to validate data in JavaScript (and TypeScript).
Superstruct is a runtime data validation library designed for JavaScript and TypeScript applications that need to validate untrusted input—like API responses, user form submissions, or configuration files. Unlike TypeScript's compile-time type checking, Superstruct validates data at runtime, throwing detailed errors when data doesn't match your defined schema. It's particularly useful in scenarios where type safety ends, such as HTTP boundaries, external APIs, or dynamic JSON parsing.
The library takes a composable approach to schema definition, using small primitive validators like string(), number(), and object() that combine into complex nested structures. This design philosophy mirrors TypeScript's type system and GraphQL's schema language, making it intuitive for developers already familiar with those tools. With 3.4 million weekly downloads, it's widely adopted in Node.js backends, REST APIs, GraphQL resolvers, and data pipeline validation layers.
Superstruct offers multiple validation methods for different use cases: assert() throws errors for invalid data, is() returns a boolean for conditional logic, create() applies coercion and defaults, and validate() returns a tuple for manual error handling. It includes zero runtime dependencies and weighs roughly 10kB gzipped, making it lighter than alternatives like Joi while maintaining full TypeScript integration. The library supports custom validators, nested schemas, and automatic type inference from schemas.
Developers choose Superstruct when they need runtime validation with minimal configuration and a familiar API surface. It's particularly effective in microservices validating inter-service communication, Express/Fastify middleware protecting routes, and serverless functions parsing event payloads. The library doesn't attempt to replace TypeScript's static analysis but complements it by catching type mismatches that only appear at runtime.
import { object, string, number, array, optional, defaulted, assert, is, create } from 'superstruct';
// Define a schema for an API endpoint receiving article data
const ArticleSchema = object({
id: number(),
title: string(),
slug: string(),
body: string(),
published: defaulted(boolean(), false),
tags: array(string()),
author: object({
id: number(),
name: string(),
email: optional(string())
}),
metadata: optional(object({
views: number(),
likes: number()
}))
});
// Example 1: Throw error if invalid (use in middleware)
try {
const payload = JSON.parse(requestBody);
assert(payload, ArticleSchema);
// payload is now validated, continue processing
} catch (error) {
console.error('Validation failed:', error.message);
// Output: "Expected number at author.id, received string"
}
// Example 2: Type guard with boolean check
function processArticle(data: unknown) {
if (is(data, ArticleSchema)) {
// TypeScript knows data matches schema here
console.log(`Processing article: ${data.title}`);
return data;
}
return null;
}
// Example 3: Apply defaults and coercion
const rawData = {
id: 42,
title: 'Understanding Superstruct',
slug: 'understanding-superstruct',
body: 'Content here...',
// published field omitted - will default to false
tags: ['validation', 'javascript'],
author: { id: 1, name: 'Jane Doe' }
};
const article = create(rawData, ArticleSchema);
console.log(article.published); // false (applied default)
// Example 4: Manual error handling without throwing
const [error, result] = validate(untrustedInput, ArticleSchema);
if (error) {
return res.status(400).json({
error: 'Invalid article data',
details: error.failures().map(f => ({
path: f.path.join('.'),
message: f.message
}))
});
}
// result contains validated dataAPI Request Validation: Validate incoming HTTP request bodies in Express, Fastify, or Next.js API routes before processing. A REST endpoint receiving user registration data can use Superstruct to assert required fields, validate email formats, and enforce password rules, returning detailed 400 errors with field-specific messages when validation fails.
GraphQL Resolver Guards: Protect GraphQL resolvers by validating resolver arguments and external data sources. When fetching user data from a third-party API, use Superstruct to ensure the response matches your expected schema before returning it to clients, preventing null reference errors and type mismatches downstream.
Configuration File Parsing: Validate application configuration loaded from JSON, YAML, or environment variables at startup. A Node.js service can define schemas for database connection strings, feature flags, and service endpoints, failing fast with clear errors if configuration is malformed rather than crashing at runtime.
Form Submission Processing: Validate client-submitted form data in server-side handlers or serverless functions. An e-commerce checkout flow can validate shipping addresses, payment details, and order items, applying defaults for optional fields and coercing string inputs to numbers where appropriate.
ETL Data Pipelines: Validate data at each stage of extract-transform-load processes. When processing CSV uploads or external data feeds, Superstruct can filter out malformed rows, log validation errors with precise paths to bad data, and ensure only clean records enter your database or analytics systems.
Object schema validation
The modular and type safe schema library for validating structural data
Dead simple Object schema validation
TypeScript-first schema declaration and validation library with static type inference
npm install superstructpnpm add superstructbun add superstruct