Structured Outputs

Enforce strict type safety and JSON Schema compliance on AI model outputs using Zod validation and structured decoding.

TL;DR

  1. Describe the shape you want with a zod schema.
  2. Enable the provider strict structured-output mode so replies always match.
  3. Verify with safeParse before trusting any model data.

Zod Schema Definition

    Declare Target Schema

    Define type-safe entity schema with strict field descriptions.

    import { z } from 'zod';
    
    const UserProfileSchema = z.object({
      name: z.string().describe('Full name'),
      age: z.number().int().positive(),
      skills: z.array(z.string()),
    });
    type UserProfile = z.infer<typeof UserProfileSchema>;
    Enforce Strict Object

    Prevent models from generating extra unverified object keys.

    const StrictSchema = z.object({
      status: z.enum(['active', 'pending']),
      score: z.number().min(0).max(100),
    }).strict();
    Nested Complex Schemas

    Compose multi-level hierarchical structures for complex data.

    const ProjectSchema = z.object({
      title: z.string(),
      tasks: z.array(z.object({
        id: z.string(),
        done: z.boolean(),
      })),
    });

OpenAI Structured Outputs

    zodResponseFormat Helper

    Pass Zod schemas directly into chat completions request format.

    import { zodResponseFormat } from 'openai/helpers/zod';
    
    const fmt = zodResponseFormat(
      UserProfileSchema,
      'profile'
    );
    const res = await openai.beta.chat.completions.parse({
      model: 'gpt-6.1-sol',
      messages: [{ role: 'user', content: 'John, 30' }],
      response_format: fmt,
    });
    Direct Parsed Access

    Read pre-validated typed object directly from completion message.

    const p: UserProfile = res.choices[0].message.parsed!;
    console.log(p.name, p.age);
    Refusal Handling

    Check if safety policies prevented structured generation.

    if (res.choices[0].message.refusal) {
      console.warn('Safety policy prevented generation');
    }

JSON Schema Native Mode

    Raw JSON Schema

    Declare raw schema parameters with strict enforcement enabled.

    const format = {
      type: 'json_schema' as const,
      json_schema: {
        name: 'user_data',
        strict: true,
        schema: { type: 'object', properties: {} }
      }
    };
    Claude Structured Output

    Parse a Claude response straight into a typed Zod object.

    import { zodOutputFormat }
      from '@anthropic-ai/sdk/helpers/zod';
    
    const res = await anthropic.messages.parse({
      model: 'claude-sonnet-5-5',
      max_tokens: 1000,
      messages: [{ role: 'user', content: text }],
      output_config: {
        format: zodOutputFormat(UserProfileSchema),
      },
    });
    Read The Parsed Result

    Use the validated object, and guard the null case.

    const profile = res.parsed_output;
    if (!profile) throw new Error('Parse failed');
    console.log(profile.name);

Validation And Fallbacks

    SafeParse Validation

    Validate raw generated JSON strings defensively with Zod.

    const parsed = JSON.parse(rawJson);
    const result = UserProfileSchema.safeParse(parsed);
    if (!result.success) {
      console.error(result.error.format());
    }
    Markdown Fence Stripping

    Clean unwanted markdown delimiters before JSON decoding.

    // Strip backtick fences without literal triggers
    function cleanJson(raw: string): string {
      const fence = String.fromCharCode(96).repeat(3);
      return raw.replaceAll(fence, '').trim();
    }
    Schema Retry Prompt

    Re-prompt model with specific validation errors for self-correction.

    const errMsg = result.error.message;
    const fixPrompt = `Fix JSON errors: ${errMsg}`;
    const fixed = await model.complete(fixPrompt);

Tips

  1. Set strict: true on JSON Schema definitions so models reject unspecified properties and follow exact types.
  2. Add descriptive describe() comments inside schema definitions to guide model field population accurately without bloating system prompts.

Warnings

  1. Do not rely solely on simple prompt instructions like return JSON; models frequently wrap outputs in markdown code fences.
  2. Avoid optional properties without explicit default fallbacks in your zod schema to prevent unexpected downstream runtime exceptions.

In Practice

FAQ