Free & open source — no account required

v1.2.5-PRICING-19
Web & Frontend • Engineering Documentation

Superstruct Mastery: Generating Readable Schemas

This technical guide provides an in-depth analysis of the json to superstruct engine, best practices for implementation, and data security standards.

JSON to Superstruct Schema: Lightweight Runtime Validation for TypeScript

Superstruct is a TypeScript validation library built around composable, functional validators. Unlike Zod (which builds schemas with a fluent chain API) or Yup (class-based mutations), Superstruct treats schemas as plain data — every schema is just a value you can store, compose, and inspect. It's significantly smaller than Zod (~7kb vs ~57kb) and was a major influence on Zod's design. It's the right choice when bundle size matters or when you prefer the explicit assert/validate/is function model over method chaining.

Live Example: API Request Validation

// Input JSON (API request body)
{
  "user_id": "usr_4421",
  "email": "user@example.com",
  "age": 28,
  "role": "editor",
  "is_active": true,
  "bio": null
}

// Generated Superstruct Schema
import * as s from 'superstruct';

export const rootStruct = s.type({
  user_id: s.string(),
  email: s.string(),
  age: s.number(),
  role: s.string(),
  is_active: s.boolean(),
  bio: s.nullable(s.string()),
});

s.type() vs s.object(): Key Difference

The generator uses s.type() — this is important to understand:

import * as s from 'superstruct';

// s.object() — strict: rejects objects with extra keys
const StrictUser = s.object({
  email: s.string(),
  role: s.string(),
});

// s.type() — coercive: allows extra keys (useful for API responses
// where the server may add fields your schema doesn't know about)
const LooseUser = s.type({
  email: s.string(),
  role: s.string(),
});

const data = { email: 'x@x.com', role: 'admin', extra: 'ignored' };
s.assert(data, StrictUser);  // throws — "extra" key not allowed
s.assert(data, LooseUser);   // passes — extra keys are fine

Three Validation Modes: assert, validate, is

import * as s from 'superstruct';

const UserSchema = s.object({
  email: s.string(),
  age:   s.min(s.number(), 13),
  role:  s.enums(['admin', 'editor', 'viewer'] as const),
});

type User = s.Infer<typeof UserSchema>;

const input = { email: 'dev@example.com', age: 25, role: 'editor' };

// Mode 1: assert — throws StructError on failure, narrows type on success
try {
  s.assert(input, UserSchema);
  // input is now typed as User
  console.log(input.role); // 'editor'
} catch (err) {
  if (err instanceof s.StructError) {
    console.error(err.path, err.message);
  }
}

// Mode 2: validate — returns [error, value] tuple (no throw)
const [err, user] = s.validate(input, UserSchema);
if (err) {
  console.error('Validation failed:', err.failures());
} else {
  console.log(user.email); // typed as User
}

// Mode 3: is — boolean check, no error details
if (s.is(input, UserSchema)) {
  console.log('Valid:', input.role);
}

Coercion and Masking

import * as s from 'superstruct';

// coerce: transform values before validation
const NumberFromString = s.coerce(s.number(), s.string(), (v) => Number(v));
const DateFromString = s.coerce(s.date(), s.string(), (v) => new Date(v));

const EventSchema = s.object({
  id:         s.string(),
  count:      NumberFromString,     // "42" → 42
  created_at: DateFromString,       // "2024-01-01" → Date object
});

// mask: strip unknown keys before validation (like Zod's .strip())
const Masked = s.mask(input, s.object({ email: s.string() }));
// Masked only contains { email: '...' }, extra keys removed

// create: coerce + mask in one step
const validated = s.create(rawInput, EventSchema);

Superstruct vs Zod vs Yup

  • Superstruct (~7kb): Smallest bundle. Functional API — schemas are values, not class instances. Best for performance-sensitive browser code or when you need fine-grained control. Less built-in validators (no .email(), .url()) — write custom refinements.
  • Zod (~57kb): Richer API, best TypeScript inference, z.infer<> is the gold standard. More built-in validators. The default choice for new TypeScript projects.
  • Yup (~40kb): Class-based, async-first. Best Formik integration. Older API but well-battle-tested for form validation.

Frequently Asked Questions

How do I get the TypeScript type from a Superstruct schema? Use s.Infer<typeof MySchema>: type User = s.Infer<typeof UserSchema>. This derives the TypeScript type from the runtime schema — no duplicate type definitions needed.

Does Superstruct support optional fields? Yes: field: s.optional(s.string()) accepts string | undefined (key can be absent). For nullable: s.nullable(s.string()) accepts string | null. For both: s.optional(s.nullable(s.string())).

How do I add custom validation rules? Use s.refine(): const PositiveNumber = s.refine(s.number(), 'positive', v => v > 0 || 'Must be positive'). The second argument is the error type name; the third returns true on success or an error string on failure.

Is my JSON sent to a server? No. TypeMorph runs entirely in your browser — none of your data leaves your machine.

Developer FAQ

Is the processing local-only?

Absolutely. TypeMorph operates entirely within your browser's sandbox. We use Web Workers for high-performance computation without ever transmitting your JSON, SQL, or API data to a remote server.

Can I use this for enterprise projects?

Yes. The tool is designed for professional software engineers who require GDPR compliance and data privacy. It is trusted by developers at top-tier startups and financial institutions.