Generating Structured Data
While text generation is useful, many applications need structured, typed data. The AI SDK Core provides built-in support for generating schema-validated structured data through the output property on generateText and streamText.
Why Structured Data?
Structured data generation is essential for:
Data extraction : Extract information from unstructured text
Classification : Categorize content into predefined types
Synthetic data : Generate test data or examples
Form filling : Generate structured responses for forms
API responses : Create typed data for downstream systems
Basic Usage
Use Output.object() with a schema to generate structured data:
import { generateText , Output } from 'ai' ;
import { openai } from '@ai-sdk/openai' ;
import { z } from 'zod' ;
const { output } = await generateText ({
model: openai ( 'gpt-5' ),
output: Output . object ({
schema: z . object ({
recipe: z . object ({
name: z . string (),
ingredients: z . array (
z . object ({
name: z . string (),
amount: z . string ()
})
),
steps: z . array ( z . string ()),
}),
}),
}),
prompt: 'Generate a lasagna recipe.' ,
});
// output is fully typed and validated
console . log ( output . recipe . name );
console . log ( output . recipe . ingredients );
Output Types
The AI SDK supports multiple output formats through the Output object:
Output.object()
Generate a structured object matching a schema:
import { generateText , Output } from 'ai' ;
import { openai } from '@ai-sdk/openai' ;
import { z } from 'zod' ;
const { output } = await generateText ({
model: openai ( 'gpt-5' ),
output: Output . object ({
schema: z . object ({
name: z . string (),
age: z . number (),
email: z . string (). email (),
interests: z . array ( z . string ()),
}),
}),
prompt: 'Generate a user profile for a software engineer.' ,
});
// Fully typed
console . log ( output . name ); // string
console . log ( output . age ); // number
console . log ( output . interests ); // string[]
Output.array()
Generate an array of structured elements:
import { generateText , Output } from 'ai' ;
import { openai } from '@ai-sdk/openai' ;
import { z } from 'zod' ;
const { output } = await generateText ({
model: openai ( 'gpt-5' ),
output: Output . array ({
element: z . object ({
city: z . string (),
temperature: z . number (),
condition: z . string (),
}),
}),
prompt: 'List weather for San Francisco, London, and Tokyo.' ,
});
// output is an array of weather objects
for ( const weather of output ) {
console . log ( ` ${ weather . city } : ${ weather . temperature } °F, ${ weather . condition } ` );
}
Streaming Arrays
When streaming arrays, use elementStream to receive complete elements as they’re generated:
import { streamText , Output } from 'ai' ;
import { openai } from '@ai-sdk/openai' ;
import { z } from 'zod' ;
const { elementStream } = streamText ({
model: openai ( 'gpt-5' ),
output: Output . array ({
element: z . object ({
name: z . string (),
class: z . string (),
description: z . string (),
}),
}),
prompt: 'Generate 3 fantasy RPG characters.' ,
});
for await ( const character of elementStream ) {
console . log ( 'Character:' , character . name );
console . log ( 'Class:' , character . class );
console . log ( 'Description:' , character . description );
console . log ( '---' );
}
Output.choice()
Choose from a fixed set of options:
import { generateText , Output } from 'ai' ;
import { openai } from '@ai-sdk/openai' ;
const { output } = await generateText ({
model: openai ( 'gpt-5' ),
output: Output . choice ({
options: [ 'positive' , 'negative' , 'neutral' ],
}),
prompt: 'Classify the sentiment: "This product exceeded my expectations!"' ,
});
console . log ( output ); // 'positive' | 'negative' | 'neutral'
Output.json()
Generate arbitrary JSON without schema validation:
import { generateText , Output } from 'ai' ;
import { openai } from '@ai-sdk/openai' ;
const { output } = await generateText ({
model: openai ( 'gpt-5' ),
output: Output . json (),
prompt: 'Generate a JSON object with city weather data for multiple cities.' ,
});
// output is any valid JSON
console . log ( output );
// {
// "San Francisco": { "temperature": 65, "condition": "Foggy" },
// "New York": { "temperature": 72, "condition": "Sunny" }
// }
Output.json() only validates that the response is valid JSON. For type safety, use Output.object() or Output.array().
Output.text()
Generate plain text (this is the default when no output is specified):
import { generateText , Output } from 'ai' ;
import { openai } from '@ai-sdk/openai' ;
const { output } = await generateText ({
model: openai ( 'gpt-5' ),
output: Output . text (),
prompt: 'Write a haiku about programming.' ,
});
console . log ( output ); // string
Schema Definitions
The AI SDK supports multiple schema libraries:
Zod Schemas
import { z } from 'zod' ;
import { generateText , Output } from 'ai' ;
import { openai } from '@ai-sdk/openai' ;
const { output } = await generateText ({
model: openai ( 'gpt-5' ),
output: Output . object ({
schema: z . object ({
title: z . string (),
tags: z . array ( z . string ()),
published: z . boolean (),
}),
}),
prompt: 'Generate blog post metadata' ,
});
JSON Schema
import { generateText , Output , jsonSchema } from 'ai' ;
import { openai } from '@ai-sdk/openai' ;
const { output } = await generateText ({
model: openai ( 'gpt-5' ),
output: Output . object ({
schema: jsonSchema ({
type: 'object' ,
properties: {
name: { type: 'string' },
age: { type: 'number' },
},
required: [ 'name' , 'age' ],
}),
}),
prompt: 'Generate a person profile' ,
});
Property Descriptions
Add descriptions to schema properties to guide the model:
import { z } from 'zod' ;
import { generateText , Output } from 'ai' ;
import { openai } from '@ai-sdk/openai' ;
const { output } = await generateText ({
model: openai ( 'gpt-5' ),
output: Output . object ({
schema: z . object ({
name: z . string (). describe ( 'The name of the recipe' ),
servings: z . number (). describe ( 'Number of servings (1-12)' ),
ingredients: z . array (
z . object ({
name: z . string (),
amount: z . string (). describe ( 'Amount in grams or ml' ),
})
). describe ( 'List of ingredients with amounts' ),
steps: z . array ( z . string ()). describe ( 'Step-by-step cooking instructions' ),
}),
}),
prompt: 'Generate a pasta recipe for 4 people.' ,
});
Descriptions help:
Clarify ambiguous property names
Specify expected formats
Provide context for nested structures
Output Name and Description
Provide a name and description for the output to improve generation quality:
import { generateText , Output } from 'ai' ;
import { openai } from '@ai-sdk/openai' ;
import { z } from 'zod' ;
const { output } = await generateText ({
model: openai ( 'gpt-5' ),
output: Output . object ({
name: 'Recipe' ,
description: 'A complete recipe with ingredients and steps' ,
schema: z . object ({
name: z . string (),
ingredients: z . array ( z . object ({ name: z . string (), amount: z . string () })),
steps: z . array ( z . string ()),
}),
}),
prompt: 'Generate a recipe for chocolate cake.' ,
});
Streaming Structured Data
Stream structured data as it’s generated using streamText:
import { streamText , Output } from 'ai' ;
import { openai } from '@ai-sdk/openai' ;
import { z } from 'zod' ;
const { partialOutputStream } = streamText ({
model: openai ( 'gpt-5' ),
output: Output . object ({
schema: z . object ({
recipe: z . object ({
name: z . string (),
ingredients: z . array (
z . object ({ name: z . string (), amount: z . string () })
),
steps: z . array ( z . string ()),
}),
}),
}),
prompt: 'Generate a lasagna recipe.' ,
});
// Stream partial objects as they're built
for await ( const partialObject of partialOutputStream ) {
console . log ( partialObject );
// { recipe: { name: "Lasagna" } }
// { recipe: { name: "Lasagna", ingredients: [{ name: "pasta" }] } }
// ...
}
Partial outputs cannot be validated against the schema since they’re incomplete. Validation happens on the final output.
Structured output can be combined with tool calling:
import { generateText , Output , tool , stepCountIs } from 'ai' ;
import { openai } from '@ai-sdk/openai' ;
import { z } from 'zod' ;
const { output } = await generateText ({
model: openai ( 'gpt-5' ),
tools: {
weather: tool ({
description: 'Get weather for a location' ,
inputSchema: z . object ({ location: z . string () }),
execute : async ({ location }) => {
return { temperature: 72 , condition: 'sunny' };
},
}),
},
output: Output . object ({
schema: z . object ({
summary: z . string (),
recommendation: z . string (),
}),
}),
stopWhen: stepCountIs ( 5 ),
prompt: 'What should I wear in San Francisco today?' ,
});
console . log ( output . summary );
console . log ( output . recommendation );
Generating structured output counts as a step. Configure stopWhen to allow enough steps for both tool execution and output generation.
Error Handling
When generateText cannot generate valid structured data, it throws NoObjectGeneratedError:
import { generateText , Output , NoObjectGeneratedError } from 'ai' ;
import { openai } from '@ai-sdk/openai' ;
import { z } from 'zod' ;
try {
const { output } = await generateText ({
model: openai ( 'gpt-5' ),
output: Output . object ({
schema: z . object ({
name: z . string (),
age: z . number (),
}),
}),
prompt: 'Generate a person' ,
});
} catch ( error ) {
if ( NoObjectGeneratedError . isInstance ( error )) {
console . log ( 'Failed to generate object' );
console . log ( 'Cause:' , error . cause );
console . log ( 'Text:' , error . text );
console . log ( 'Usage:' , error . usage );
}
}
For streaming, use the onError callback:
import { streamText , Output } from 'ai' ;
import { openai } from '@ai-sdk/openai' ;
import { z } from 'zod' ;
const result = streamText ({
model: openai ( 'gpt-5' ),
output: Output . object ({
schema: z . object ({ name: z . string () }),
}),
prompt: 'Generate data' ,
onError ({ error }) {
console . error ( 'Stream error:' , error );
},
});
Examples
import { generateText , Output } from 'ai' ;
import { openai } from '@ai-sdk/openai' ;
import { z } from 'zod' ;
const { output } = await generateText ({
model: openai ( 'gpt-5' ),
output: Output . object ({
schema: z . object ({
company: z . string (),
position: z . string (),
location: z . string (),
salary: z . string (). optional (),
requirements: z . array ( z . string ()),
}),
}),
prompt: `Extract job posting details from:
"We're hiring a Senior Software Engineer in San Francisco.
Requirements: 5+ years experience, TypeScript, React.
Competitive salary."` ,
});
console . log ( output );
Content Classification
import { generateText , Output } from 'ai' ;
import { openai } from '@ai-sdk/openai' ;
import { z } from 'zod' ;
const { output } = await generateText ({
model: openai ( 'gpt-5' ),
output: Output . object ({
schema: z . object ({
category: z . enum ([ 'technology' , 'business' , 'health' , 'entertainment' ]),
confidence: z . number (). min ( 0 ). max ( 1 ),
keywords: z . array ( z . string ()),
}),
}),
prompt: 'Classify this article: "New AI model achieves breakthrough in natural language understanding"' ,
});
console . log ( `Category: ${ output . category } ` );
console . log ( `Confidence: ${ output . confidence } ` );
Next Steps
Tool Calling Combine structured output with tools
Prompt Engineering Tips for better structured data generation
Settings Configure generation parameters