{ } Schemato

Guide

Infer optional fields from multiple JSON samples

Real API responses are uneven. One user has an email, another has a team object, and a third response omits both. Paste several samples as NDJSON so Schemato can generate one safer shared type.

Try it now in JSON → TypeScript, JSON → Zod, or JSON → Go struct.


The single-sample problem

A JSON converter can only infer what the pasted sample proves. If the first response has email and role, a single-sample output may make both fields look required even if the API often omits them.

export interface User {
  id: number;
  name: string;
  email: string;
  role: string;
}

Paste several responses as NDJSON

Put each complete JSON object on its own line. The lines do not need to share the same fields; that difference is exactly what helps infer optional properties.

{"id":1,"name":"Ada","email":"ada@example.com","role":"admin"}
{"id":2,"name":"Linus","lastLoginAt":"2026-06-08T09:30:00Z"}
{"id":3,"name":"Grace","email":"grace@example.com","team":{"id":"core","name":"Platform"}}

Generated TypeScript output

Fields present in every sample stay required. Fields that appear in only some samples become optional.

export interface User {
  "id": number;
  "name": string;
  "email"?: string;
  "role"?: string;
  "lastLoginAt"?: string;
  "team"?: Team;
}

export interface Team {
  "id": string;
  "name": string;
}

Why this matters

Optional fields are where generated code most often drifts from real APIs. Multi-sample inference gives you a better first draft before you commit the type, validator, or DTO to your app.

The same merged shape works for validators

If the API response crosses a runtime boundary, generate Zod instead of plain TypeScript. The optional fields carry through to the schema.

import { z } from "zod";

export const Team = z.object({
  id: z.string(),
  name: z.string(),
});

export const User = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().optional(),
  role: z.string().optional(),
  lastLoginAt: z.string().optional(),
  team: Team.optional(),
});

And DTOs for backend languages

The same JSON input can produce Go structs, Pydantic models, Rust structs, Swift Codable types, Kotlin data classes, and more.

type User struct {
  ID          int    `json:"id"`
  Name        string `json:"name"`
  Email       *string `json:"email,omitempty"`
  Role        *string `json:"role,omitempty"`
  LastLoginAt *string `json:"lastLoginAt,omitempty"`
  Team        *Team   `json:"team,omitempty"`
}

A quick workflow

1. Collect 2-5 real API responses for the same endpoint.
2. Put each response on one line as NDJSON.
3. Paste the samples into the JSON converter.
4. Review optional fields before copying into production.
5. Switch to JSON Schema or OpenAPI when you have a formal contract.

When to use samples, schema, or OpenAPI

Use multiple samples when you only have responses

This is common when exploring a third-party API, debugging production payloads, or turning fixtures into local types.

Use JSON Schema when you have a formal contract

A schema can express required fields, enums, nullable values, refs, and unions more explicitly than examples can.

Use OpenAPI when the endpoint already has a spec

OpenAPI is the better source of truth for client and server API boundaries. Samples are the fallback when no spec exists.

FAQ

Can I paste normal formatted JSON?

Yes. A single JSON value still works as before. NDJSON is only needed when you want to merge multiple separate samples.

Can I paste a JSON array instead?

Yes. Arrays also merge item shapes. NDJSON is useful when your samples are separate responses rather than one array payload.

Does this prove every field is correct?

No. Samples improve the first draft, but they are still examples. Use API docs, JSON Schema, or OpenAPI when the contract matters.

Related tools