Structured output with zod
Describe the deck outline once with zod and use LangChain's withStructuredOutput to get a checked JavaScript object from Gemini instead of JSON text.
In lesson 7.2 you wrote the outline schema by hand in Gemini's format, called the API, and parsed the JSON text. LangChain collapses that into two steps: describe the shape, then ask for it.
zod schema
z.object({ title: z.string(), … })
JSON schema
sent with the request
Gemini
constrained to that shape
Parsed object
outline.slides[0].heading
The schema in zod
z.object({
title: z.string().describe("The deck title, under 8 words"),
slides: z.array(
z.object({
component: z.enum(SLIDE_COMPONENTS),
heading: z.string(),
points: z.array(z.string()),
}),
),
})z.object,z.array,z.stringandz.enumdescribe the same shape as before, in plain JavaScript..describe("…")adds a description to the schema Gemini receives. The model reads it, so "under 8 words" really does shorten titles.
Asking for it
model.withStructuredOutput(OutlineSchema) returns a new runnable. Invoke it with the request, and what comes back is already a JavaScript object.
Under the hood — Is the object checked against the schema?
Gemini is constrained to the JSON schema while it writes, and LangChain parses the result. If the reply somehow doesn't match (a truncated response, for example), you get an error instead of a half-built object. That's what you want: fail loudly at the boundary, not three steps later.
Key takeaways
- Describe the outline once with zod; LangChain turns it into Gemini's JSON schema.
- withStructuredOutput returns a parsed object, not a string you have to JSON.parse.
- .describe() text is part of the prompt: the model reads it.
Sign in to run the exercise
Reading is free. Writing code here needs an account so we have somewhere to keep your Gemini key and the +30 XP you are about to earn.