Skip to content
47% off: the complete course for $79 (reg. $149)Order now →
AI Builder
Module 4Full course11 min read

Structured Outputs in Practice

Get guaranteed-valid JSON from a model with schema-constrained decoding, parse it into typed objects, and know the limits of what the guarantee covers.

Most AI features don't end with text on a screen. They end with your code doing something: setting a ticket's priority, filling a form, saving a row, choosing which component to render. For that you need output a program can trust, and "please respond in JSON" doesn't give you that. It works in the demo, and then one response in a few hundred arrives wrapped in a markdown fence, or with a friendly sentence in front, or with "priority": "Urgent!!", and your parser throws in production.

This lesson shows you how to get JSON that is guaranteed to match a schema, parse it into typed TypeScript objects, and handle the cases where the guarantee doesn't hold. Just as important, you'll learn exactly what the guarantee covers, so you don't mistake well-formed output for correct output.

Why "respond in JSON" breaks in production

Before structured outputs existed, builders got JSON by asking for it. The prompt said "Return only a JSON object with these keys," and the code ran JSON.parse and hoped. A language model generates text one token at a time, choosing likely continuations. A JSON instruction makes valid JSON likely. It doesn't make it certain.

The failures are familiar to anyone who has run this at scale. What matters for a product is what each one does downstream:

What the model returnedWhat your product did
A friendly preamble, or the object wrapped in a markdown fenceJSON.parse threw, the triage worker crashed, and the ticket sat unrouted
No priority key on an ambiguous ticketundefined sorted to the bottom of the queue, so an urgent ticket waited a day
"priority": "medium-high" when you allowed four valuesThe queue filter grew a fifth option nobody could select
An object cut off mid-string at max_tokensA retry loop paid for three more calls with the same limit and got the same result

Teams used to stack defenses: regexes to strip fences, retry loops, "repair" libraries that fix trailing commas. Some of that is still useful (you'll build a proper repair loop in Validation, Retries, and Repair), but the base layer has changed. Every major provider now offers schema-constrained decoding, and it should be your default whenever code consumes the output.