Skip to main content
Instead of parsing free-form text, you can ask a model to return data that matches a specific structure. Genkit validates the output against your schema and automatically retries if the model returns invalid JSON.

How it works

When you pass an output.schema to generate(), Genkit:
  1. Injects instructions into the prompt telling the model to respond in JSON matching the schema.
  2. Parses the model response and extracts the JSON.
  3. Validates the parsed value against the schema.
  4. Retries the request (up to maxTurns times) if validation fails.
The validated object is then available on response.output.

Basic example

import { genkit, z } from 'genkit';
import { googleAI } from '@genkit-ai/google-genai';

const ai = genkit({ plugins: [googleAI()], model: 'googleai/gemini-2.0-flash' });

const RecipeSchema = z.object({
  name: z.string(),
  ingredients: z.array(z.string()),
  steps: z.array(z.string()),
  prepTimeMinutes: z.number(),
});

const response = await ai.generate({
  prompt: 'Give me a recipe for chocolate chip cookies.',
  output: {
    schema: RecipeSchema,
  },
});

// response.output is typed as z.infer<typeof RecipeSchema>
const recipe = response.output;
console.log(recipe.name);           // "Classic Chocolate Chip Cookies"
console.log(recipe.prepTimeMinutes); // 20

Output formats

The output.format field controls how Genkit instructs the model to format its response. Available formats:
FormatDescription
jsonA single JSON object (default when a schema is provided).
textPlain text (default when no schema is provided).
arrayA JSON array of objects.
enumOne of a fixed set of string values.
jsonlNewline-delimited JSON (useful for streaming structured data).

Enum output

Use format: 'enum' to constrain a response to one of a specific set of values:
const SentimentSchema = z.enum(['positive', 'negative', 'neutral']);

const response = await ai.generate({
  prompt: 'The food was cold and the service was slow.',
  output: {
    schema: SentimentSchema,
    format: 'enum',
  },
});

console.log(response.output); // "negative"

Array output

Use format: 'array' to request a JSON array. Pair it with a Zod array schema:
const TagListSchema = z.array(z.string());

const response = await ai.generate({
  prompt: 'List 5 keywords for a blog post about async JavaScript.',
  output: {
    schema: TagListSchema,
    format: 'array',
  },
});

console.log(response.output); // ["async", "await", "promises", "event loop", "callbacks"]

Constrained generation

Some models support native constrained generation — the model is instructed at the inference level to only produce tokens that are valid for the given schema. This is more reliable than prompt-based instructions. Set output.constrained: true to enable it when available:
const response = await ai.generate({
  prompt: 'Extract the order details from this receipt: ...',
  output: {
    schema: OrderSchema,
    constrained: true,
  },
});
Not all models support constrained generation. Genkit falls back to prompt-based instructions when the model does not support it. You can check model.supports.constrained to see what a model supports.

Extracting structured data from text

A common use case is extracting structured data from unstructured input such as an email, document, or web page.
const ContactSchema = z.object({
  name: z.string(),
  email: z.string().email().optional(),
  phone: z.string().optional(),
  company: z.string().optional(),
});

const emailText = `
  Hi, I'm Sarah Connor from Cyberdyne Systems.
  Reach me at sarah@cyberdyne.com or 555-0100.
`;

const response = await ai.generate({
  prompt: `Extract the contact information from the following text:\n${emailText}`,
  output: {
    schema: ContactSchema,
  },
});

console.log(response.output);
// {
//   name: "Sarah Connor",
//   email: "sarah@cyberdyne.com",
//   phone: "555-0100",
//   company: "Cyberdyne Systems"
// }

Accessing the output

The GenerateResponse object exposes several accessors:
PropertyTypeDescription
response.outputT | nullThe validated structured output. null if parsing failed.
response.textstringRaw text from the model (all text parts concatenated).
response.dataT | nullThe first data part of the message (for models that return typed data natively).
response.messageMessageThe full generated message object.

Schema validation and retries

Genkit calls response.assertValidSchema() internally. If the model returns output that fails schema validation, Genkit throws a GenkitError. You can call response.isValid() to check without throwing:
const response = await ai.generate({
  prompt: '...',
  output: { schema: MySchema },
});

if (!response.isValid()) {
  // Handle invalid output
  console.error('Model returned invalid output');
} else {
  const data = response.output!;
}
For more reliable structured output, use Gemini models with constrained: true. These models support native JSON mode and are less likely to produce invalid output.

Using schemas in flows

Structured output works inside flows just like it does in standalone generate() calls. Define the flow’s output schema with Zod and use it in the generate call:
const extractContactFlow = ai.defineFlow(
  {
    name: 'extractContact',
    inputSchema: z.string(),
    outputSchema: ContactSchema,
  },
  async (text) => {
    const response = await ai.generate({
      prompt: `Extract contact info from: ${text}`,
      output: { schema: ContactSchema },
    });
    return response.output!;
  }
);

Streaming

Stream structured output chunks as they arrive.

Flows

Wrap generate calls in type-safe, observable flows.

Prompts

Define output schemas in .prompt files.

Models

See which models support constrained generation.