Module 1 - Lesson 3f: Structured Output
Validated JSON output using Zod.
Published: 1/3/2026
Example 6: Structured Output with Zod
Structured output ensures the AI returns data in a specific, validated format. We use Zod schemas to define and validate the structure.
What It Does
Uses Zod to define a schema for the response, ensuring it's always in the correct format. Perfect for APIs, databases, or when you need consistent data structures.
Code Snippet
Create src/structured-output-prompt.ts:
import OpenAI from "openai"; import dotenv from "dotenv"; import { zodTextFormat } from "openai/helpers/zod"; import { z } from "zod"; dotenv.config(); // Define the expected output structure using Zod const ChristmasMarkets = z.object({ country: z.string().describe("Country where the market is located"), cityMarkets: z .array( z.object({ city: z.string().describe("City where the market is located"), markets: z .array( z.object({ name: z.string().describe("Name of the Christmas market"), description: z .string() .describe("Description of the Christmas market"), address: z.string().describe("Address of the Christmas market"), }) ) .describe("List of Christmas markets in the city"), }) ) .describe("List of cities with their Christmas markets"), }); const openai = new OpenAI(); async function structuredOutputPrompt(country: string): Promise<void> { try { console.log("Testing OpenAI connection..."); // Use parse() instead of create() for structured output const response = await openai.responses.parse({ model: "gpt-5-nano", input: [ { role: "system", content: "You are a helpful travel assistant. Extract the relevant Christmas market information based on user input for a country and information in a structured JSON format.", }, { role: "user", content: `provide a list of famous Christmas markets in ${country} with a brief description and address for each market.`, }, ], // Use zodTextFormat to enforce the schema text: { format: zodTextFormat(ChristmasMarkets, "markets") }, }); // Get the parsed, validated output const markets = response.output_parsed; console.log("✅ Structured Output Success!"); console.log("AI Response (validated JSON):"); console.log(JSON.stringify(markets, null, 2)); console.log("Tokens used:"); console.dir(response.usage, { depth: 10 }); } catch (error) { if (error instanceof OpenAI.APIError) { console.log("❌ API Error:", error.status, error.message); } else if (error instanceof Error) { console.log("❌ Error:", error.message); } } } // Run with country argument (defaults to "Austria") const country = process.argv[2] ?? "Austria"; structuredOutputPrompt(country).catch(console.error);
Run It
# Default (Austria) pnpm tsx src/structured-output-prompt.ts # Custom country pnpm tsx src/structured-output-prompt.ts "Germany"
Key Points
- Zod schemas: Define expected structure
- Type safety: TypeScript knows the exact shape
- Validation: Ensures response matches schema
- Use case: APIs, databases, data processing
Zod Schema Tips
// Simple object z.object({ name: z.string(), age: z.number(), }); // With descriptions (helps AI understand) z.object({ name: z.string().describe("Person's full name"), age: z.number().describe("Age in years"), }); // Arrays z.array(z.string()); // Array of strings // Nested objects z.object({ address: z.object({ street: z.string(), city: z.string(), }), });