JSON Mode & Structured Outputs

Published

In tutorials 3.1 and 3.2, you learned how AI can call functions and use multiple tools. But what happens when you need the AI to return data in a specific format every single time? What if you're building a system that processes AI responses automatically?

This is where JSON Mode and Structured Outputs become essential. Instead of getting unpredictable text responses, you can force the AI to return perfectly formatted, consistent data structures.

Why Structured Outputs Matter

Think of the difference like this:

Without Structure (Unpredictable):

User: "Extract the name, age, and email from: John Smith, 25 years old, john@email.com"
AI: "The person's name is John Smith, they are 25 years old, and their email address is john@email.com."

With Structure (Predictable):

User: Same request
AI: {"name": "John Smith", "age": 25, "email": "john@email.com"}

The second format can be directly processed by your code, stored in databases, or passed to other systems without any parsing headaches.

Understanding JSON Mode vs Structured Outputs

There are two main approaches to getting structured data:

  1. JSON Mode: Tell the AI "respond only in JSON format"
  2. Structured Outputs: Define exact schemas the AI must follow

JSON Mode is simpler but less reliable - the AI tries to return JSON but might make mistakes.

Structured Outputs are more powerful - you define the exact structure, and the AI is forced to follow it perfectly.

The Structured Data Flow

graph TD
    A[User Request] --> B[AI Analyzes Intent]
    B --> C[AI Generates Response]
    C --> D{Structure Required?}
    D -->|JSON Mode| E[AI Formats as JSON]
    D -->|Schema Mode| F[AI Follows Exact Schema]
    E --> G[Validate JSON]
    F --> H[Guaranteed Valid Structure]
    G --> I[Process Data]
    H --> I

Environment Setup

Build on your existing setup from previous tutorials:

# You should already have this from 3.1 and 3.2
npm install @google/genai dotenv
npm install -D typescript @types/node ts-node

Working Code Example

Let's build a data extraction system that returns perfectly structured information. We'll create examples that show both JSON mode and schema-based approaches.

Step 1: Import Dependencies and Setup

import { GoogleGenAI, Type } from "@google/genai";
import * as readline from "readline";
import * as dotenv from "dotenv";

dotenv.config();

Notice we're importing Type - this is what we'll use to define exact data structures, just like in tutorial 3.2.

Step 2: Define Data Schemas

// Schema for extracting person information
const personSchema = {
  type: Type.OBJECT,
  properties: {
    name: { type: Type.STRING, description: "Full name of the person" },
    age: { type: Type.INTEGER, description: "Age in years" },
    email: { type: Type.STRING, description: "Email address" },
    phone: { type: Type.STRING, description: "Phone number (optional)" },
    occupation: {
      type: Type.STRING,
      description: "Job or profession (optional)",
    },
  },
  required: ["name", "age", "email"],
};

// Schema for analyzing product reviews
const reviewAnalysisSchema = {
  type: Type.OBJECT,
  properties: {
    sentiment: {
      type: Type.STRING,
      enum: ["positive", "negative", "neutral"],
      description: "Overall sentiment of the review",
    },
    rating: {
      type: Type.INTEGER,
      description: "Estimated rating from 1-5 based on review content",
    },
    keyPoints: {
      type: Type.ARRAY,
      items: { type: Type.STRING },
      description: "Main points mentioned in the review",
    },
    categories: {
      type: Type.ARRAY,
      items: {
        type: Type.STRING,
        enum: ["quality", "price", "service", "delivery", "features"],
      },
      description: "Categories the review mentions",
    },
  },
  required: ["sentiment", "rating", "keyPoints"],
};

These schemas define exactly what structure the AI must return. The AI cannot deviate from these formats.

Step 3: Setup AI Client

const apiKey = process.env.GEMINI_API_KEY;
if (!apiKey) {
  console.error("Error: GEMINI_API_KEY not found");
  process.exit(1);
}

const genAI = new GoogleGenAI({ apiKey });

Step 4: JSON Mode Implementation

// Simple JSON mode - AI tries to return JSON
async function extractWithJSONMode(text: string): Promise<void> {
  try {
    console.log("🔄 Using JSON Mode...");

    const prompt = `Extract person information from this text and return ONLY valid JSON: "${text}"
    
    Return format: {"name": "...", "age": ..., "email": "...", "phone": "...", "occupation": "..."}`;

    const response = await genAI.models.generateContentStream({
      model: "gemini-2.5-flash",
      contents: [{ text: prompt }],
      config: {
        temperature: 0.1,
        responseMimeType: "application/json", // This enables JSON mode
      },
    });

    let fullResponse = "";
    process.stdout.write("AI Response: ");

    for await (const chunk of response) {
      const chunkText = chunk.text || "";
      process.stdout.write(chunkText);
      fullResponse += chunkText;
    }

    console.log("\n");

    // Try to parse the JSON
    try {
      const parsed = JSON.parse(fullResponse);
      console.log("✅ Successfully parsed JSON:", parsed);
    } catch (parseError) {
      console.log("❌ Failed to parse JSON:", parseError);
    }
  } catch (error) {
    console.log(
      "Error:",
      error instanceof Error ? error.message : "Something went wrong"
    );
  }
}

JSON Mode tells the AI to respond in JSON format, but it's not guaranteed to be valid or follow a specific structure.

Step 5: Structured Output Implementation

// Structured output with schema - guaranteed format
async function extractWithSchema(text: string): Promise<void> {
  try {
    console.log("🔄 Using Structured Output Schema...");

    const response = await genAI.models.generateContentStream({
      model: "gemini-2.5-flash",
      contents: [
        {
          text: `Extract person information from this text: "${text}"`,
        },
      ],
      config: {
        temperature: 0.1,
        responseSchema: personSchema, // This enforces the exact schema
        responseMimeType: "application/json",
      },
    });

    let fullResponse = "";
    process.stdout.write("AI Response: ");

    for await (const chunk of response) {
      const chunkText = chunk.text || "";
      process.stdout.write(chunkText);
      fullResponse += chunkText;
    }

    console.log("\n");

    // Parse the guaranteed-valid JSON
    const parsed = JSON.parse(fullResponse);
    console.log("✅ Structured data extracted:", parsed);

    // Validate required fields are present
    if (parsed.name && parsed.age && parsed.email) {
      console.log("✅ All required fields present");
    } else {
      console.log("❌ Missing required fields");
    }
  } catch (error) {
    console.log(
      "Error:",
      error instanceof Error ? error.message : "Something went wrong"
    );
  }
}

With structured outputs, the AI is forced to follow the exact schema. The response is guaranteed to be valid JSON in the specified format.

Step 6: Advanced Example - Review Analysis

// Complex structured output for review analysis
async function analyzeReview(reviewText: string): Promise<void> {
  try {
    console.log("🔄 Analyzing review with structured output...");

    const response = await genAI.models.generateContentStream({
      model: "gemini-2.5-flash",
      contents: [
        {
          text: `Analyze this product review and extract structured information: "${reviewText}"`,
        },
      ],
      config: {
        temperature: 0.1,
        responseSchema: reviewAnalysisSchema,
        responseMimeType: "application/json",
      },
    });

    let fullResponse = "";
    process.stdout.write("Analysis: ");

    for await (const chunk of response) {
      const chunkText = chunk.text || "";
      process.stdout.write(chunkText);
      fullResponse += chunkText;
    }

    console.log("\n");

    const analysis = JSON.parse(fullResponse);

    // Display formatted results
    console.log("📊 Review Analysis Results:");
    console.log(`   Sentiment: ${analysis.sentiment}`);
    console.log(`   Rating: ${analysis.rating}/5`);
    console.log(`   Key Points: ${analysis.keyPoints.join(", ")}`);
    if (analysis.categories) {
      console.log(`   Categories: ${analysis.categories.join(", ")}`);
    }
  } catch (error) {
    console.log(
      "Error:",
      error instanceof Error ? error.message : "Something went wrong"
    );
  }
}

This shows how structured outputs can handle complex, nested data with arrays and enums.

Step 7: Interactive Demo System

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

function startDemo(): void {
  console.log("🎯 JSON Mode & Structured Outputs Demo");
  console.log("Choose an option:");
  console.log("1. Extract person info (JSON Mode)");
  console.log("2. Extract person info (Structured Output)");
  console.log("3. Analyze product review");
  console.log("4. Exit\n");

  const handleChoice = (): void => {
    rl.question("Enter choice (1-4): ", async (choice) => {
      switch (choice) {
        case "1":
          rl.question("Enter text with person info: ", async (text) => {
            await extractWithJSONMode(text);
            console.log("\n" + "=".repeat(50) + "\n");
            handleChoice();
          });
          break;

        case "2":
          rl.question("Enter text with person info: ", async (text) => {
            await extractWithSchema(text);
            console.log("\n" + "=".repeat(50) + "\n");
            handleChoice();
          });
          break;

        case "3":
          rl.question("Enter product review: ", async (review) => {
            await analyzeReview(review);
            console.log("\n" + "=".repeat(50) + "\n");
            handleChoice();
          });
          break;

        case "4":
          console.log("Goodbye!");
          rl.close();
          return;

        default:
          console.log("Invalid choice. Please enter 1-4.");
          handleChoice();
      }
    });
  };

  handleChoice();
}

startDemo();

Testing Your Structured Output System

Here's what the system looks like in action:

🎯 JSON Mode & Structured Outputs Demo
Choose an option:
1. Extract person info (JSON Mode)
2. Extract person info (Structured Output)
3. Analyze product review
4. Exit

Enter choice (1-4): 2
Enter text with person info: Sarah Johnson is 28 years old, works as a software engineer, and can be reached at sarah.j@techcorp.com or 555-0123

🔄 Using Structured Output Schema...
AI Response: {"name":"Sarah Johnson","age":28,"email":"sarah.j@techcorp.com","phone":"555-0123","occupation":"software engineer"}

✅ Structured data extracted: {
  name: 'Sarah Johnson',
  age: 28,
  email: 'sarah.j@techcorp.com',
  phone: '555-0123',
  occupation: 'software engineer'
}
✅ All required fields present

Enter choice (1-4): 3
Enter product review: This laptop is amazing! Great performance, fast delivery, but a bit expensive. The customer service was helpful when I had questions.

🔄 Analyzing review with structured output...
Analysis: {"sentiment":"positive","rating":4,"keyPoints":["amazing performance","fast delivery","expensive price","helpful customer service"],"categories":["quality","delivery","price","service"]}

📊 Review Analysis Results:
   Sentiment: positive
   Rating: 4/5
   Key Points: amazing performance, fast delivery, expensive price, helpful customer service
   Categories: quality, delivery, price, service

Key Differences: JSON Mode vs Structured Outputs

JSON Mode:

  • ✅ Simple to implement
  • ✅ Flexible format
  • ❌ Not guaranteed to be valid JSON
  • ❌ Structure can vary
  • ❌ Requires error handling for parsing

Structured Outputs:

  • ✅ Guaranteed valid JSON
  • ✅ Exact schema compliance
  • ✅ No parsing errors
  • ✅ Consistent data structure
  • ❌ Less flexible
  • ❌ Requires schema definition

Real-World Applications

Data Processing Pipelines: Extract structured data from documents, emails, or forms for automated processing.

API Responses: Ensure your AI-powered APIs always return consistent, predictable data formats.

Database Integration: Generate data that can be directly inserted into databases without transformation.

Analytics Systems: Extract structured insights from unstructured text for reporting and analysis.

Common Pitfalls to Avoid

Over-complex schemas: Start simple and add complexity gradually. Complex schemas can confuse the AI.

Missing required fields: Always define which fields are required vs optional in your schema.

Wrong data types: Be specific about types (string vs integer vs array) to avoid parsing issues.

Ignoring validation: Even with structured outputs, validate the data makes logical sense (e.g., age > 0).

FAQ

Summary

You've learned to control AI output formats with precision using JSON Mode and Structured Outputs. The key concepts are: JSON Mode provides flexible JSON formatting but without guarantees, Structured Outputs enforce exact schemas for reliable data processing, schema design determines the quality and consistency of extracted data, and combining structured outputs with streaming maintains real-time user experience. This structured approach transforms unpredictable AI text into reliable, processable data that can power automated systems and data pipelines.

Complete Code

You can find the complete, runnable code for this tutorial on GitHub: https://github.com/avestalabs/academy/tree/main/3-structured-interactions/json-mode-structured-outputs

Share this article: