GEMINI LABJP
SIRI — WWDC 2026 confirms the revamped Siri runs on a Google Gemini model, though it won't ship in the EU at iOS 27 due to the DMAFLASH3.5 — Gemini 3.5 Flash is now GA, the top Flash model for sustained frontier performance on agentic and coding tasksIMAGE-GA — Gemini 3.1 Flash Image and 3.1 Pro Image are GA as native visual models; the preview versions shut down Jun 25MANAGED-AGENTS — Managed Agents launch in public preview in the Gemini API, running autonomous agents in Google-hosted isolated Linux sandboxesFILE-SEARCH — File Search now supports multimodal search, with native image embedding and retrieval via gemini-embedding-2DEPRECATION — gemini-3.1-flash-image-preview and gemini-3-pro-image-preview shut down Jun 25 — migrate to the GA models soonSIRI — WWDC 2026 confirms the revamped Siri runs on a Google Gemini model, though it won't ship in the EU at iOS 27 due to the DMAFLASH3.5 — Gemini 3.5 Flash is now GA, the top Flash model for sustained frontier performance on agentic and coding tasksIMAGE-GA — Gemini 3.1 Flash Image and 3.1 Pro Image are GA as native visual models; the preview versions shut down Jun 25MANAGED-AGENTS — Managed Agents launch in public preview in the Gemini API, running autonomous agents in Google-hosted isolated Linux sandboxesFILE-SEARCH — File Search now supports multimodal search, with native image embedding and retrieval via gemini-embedding-2DEPRECATION — gemini-3.1-flash-image-preview and gemini-3-pro-image-preview shut down Jun 25 — migrate to the GA models soon
Articles/API / SDK
API / SDK/2026-04-01Beginner

Using Gemini API with Spring Boot: A Java Developer's Guide to Building AI Chat Applications

A step-by-step guide to integrating Gemini API with Spring Boot. Learn how to set up a production-ready REST endpoint that delivers Gemini AI responses — no Python required.

gemini-api285spring-boot2java2tutorial19rest-api2

Why Java Developers Should Use the Gemini API

When it comes to building AI-powered applications, Python tends to dominate the conversation. But the reality is that a huge portion of enterprise backends — banking systems, healthcare platforms, logistics services — are built with Java and Spring Boot. If you can integrate the Gemini API directly into your existing Java stack, you get powerful AI capabilities without switching languages or fragmenting your infrastructure.

In this guide, we'll walk through building a simple REST endpoint with Spring Boot that accepts a message and returns a Gemini AI response. This foundation can be extended into customer support bots, internal document Q&A systems, code review assistants, and much more.

If you're brand new to the Gemini API, start with the Gemini API Quickstart guide to get your API key set up first.


Prerequisites

Before diving in, make sure you have the following ready:

  • Java 17 or later (LTS recommended)
  • Maven 3.8+ or Gradle 8+
  • A Google AI Studio account with an API key
  • Spring Boot 3.2+ (the version used in this guide)

Getting Your Gemini API Key

Head to Google AI Studio and click "Get API Key" to generate a new key. You'll add it to your application.properties file shortly.


Setting Up the Spring Boot Project

Configuring pom.xml

Generate a project using Spring Initializr and add the following dependencies. We'll use WebClient from Spring WebFlux to communicate with the Gemini API.

<!-- pom.xml (excerpt) -->
<dependencies>
  <!-- Spring Web for REST API exposure -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
 
  <!-- WebFlux for WebClient (non-blocking HTTP) -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
  </dependency>
 
  <!-- JSON processing -->
  <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
  </dependency>
</dependencies>

Configuring application.properties

Store your API key in the configuration file rather than hardcoding it.

# src/main/resources/application.properties
 
# Gemini API settings
gemini.api.key=YOUR_GEMINI_API_KEY
gemini.api.url=https://generativelanguage.googleapis.com/v1beta
gemini.model=gemini-2.5-flash
 
# Application settings
spring.application.name=gemini-chat-api
server.port=8080

Important: Replace YOUR_GEMINI_API_KEY with your actual key. In production, use environment variables or a secrets manager — never commit API keys to version control.


Implementing the Gemini API Client

Configuration Class (GeminiConfig.java)

package com.example.geminichat.config;
 
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;
 
@Configuration
public class GeminiConfig {
 
    @Value("${gemini.api.url}")
    private String apiUrl;
 
    // Register WebClient as a Spring Bean
    @Bean
    public WebClient geminiWebClient() {
        return WebClient.builder()
                .baseUrl(apiUrl)
                .defaultHeader("Content-Type", "application/json")
                .build();
    }
}

Data Models

Define Java Records that match the Gemini API's JSON request/response format.

package com.example.geminichat.model;
 
import java.util.List;
 
// Request body from the client
public record ChatRequest(String message) {}
 
// Request body sent to Gemini API
public record GeminiRequest(List<Content> contents) {
    public record Content(List<Part> parts) {}
    public record Part(String text) {}
}
 
// Gemini API response (simplified)
public record GeminiResponse(List<Candidate> candidates) {
    public record Candidate(Content content) {}
    public record Content(List<Part> parts) {}
    public record Part(String text) {}
}
 
// Response returned to the client
public record ChatResponse(String reply, boolean success) {}

Service Layer (GeminiService.java)

Encapsulate the Gemini API communication logic in a dedicated service class.

package com.example.geminichat.service;
 
import com.example.geminichat.model.ChatResponse;
import com.example.geminichat.model.GeminiRequest;
import com.example.geminichat.model.GeminiResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
 
import java.util.List;
 
@Service
public class GeminiService {
 
    private final WebClient webClient;
 
    @Value("${gemini.api.key}")
    private String apiKey;
 
    @Value("${gemini.model}")
    private String model;
 
    public GeminiService(WebClient geminiWebClient) {
        this.webClient = geminiWebClient;
    }
 
    public ChatResponse chat(String userMessage) {
        // Build the request body
        var request = new GeminiRequest(
            List.of(new GeminiRequest.Content(
                List.of(new GeminiRequest.Part(userMessage))
            ))
        );
 
        try {
            // POST to Gemini API
            GeminiResponse response = webClient.post()
                    .uri("/models/{model}:generateContent?key={key}", model, apiKey)
                    .bodyValue(request)
                    .retrieve()
                    .bodyToMono(GeminiResponse.class)
                    .block(); // Synchronous for simplicity — go reactive in production
 
            // Extract text from response
            if (response != null && !response.candidates().isEmpty()) {
                String text = response.candidates().get(0)
                        .content().parts().get(0).text();
                return new ChatResponse(text, true);
            }
            return new ChatResponse("No response received from Gemini", false);
 
        } catch (Exception e) {
            return new ChatResponse("Error: " + e.getMessage(), false);
        }
    }
}

REST Controller

package com.example.geminichat.controller;
 
import com.example.geminichat.model.ChatRequest;
import com.example.geminichat.model.ChatResponse;
import com.example.geminichat.service.GeminiService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
 
@RestController
@RequestMapping("/api/chat")
public class ChatController {
 
    private final GeminiService geminiService;
 
    public ChatController(GeminiService geminiService) {
        this.geminiService = geminiService;
    }
 
    // POST /api/chat
    @PostMapping
    public ResponseEntity<ChatResponse> chat(@RequestBody ChatRequest request) {
        if (request.message() == null || request.message().isBlank()) {
            return ResponseEntity.badRequest()
                    .body(new ChatResponse("Message cannot be empty", false));
        }
 
        ChatResponse response = geminiService.chat(request.message());
        return ResponseEntity.ok(response);
    }
 
    // Health check
    @GetMapping("/health")
    public ResponseEntity<String> health() {
        return ResponseEntity.ok("Gemini Chat API is running!");
    }
}

Testing the API

Start the application and test it with curl.

# Start the application
./mvnw spring-boot:run
 
# Send a chat request
curl -X POST http://localhost:8080/api/chat \
  -H "Content-Type: application/json" \
  -d '{"message": "What are the top 3 features of Java?"}'

Expected response:

{
  "reply": "Here are the top 3 features of Java:\n1. **Platform Independence**: Java's 'Write Once, Run Anywhere' philosophy means your compiled bytecode runs on any platform with a JVM.\n2. **Object-Oriented Design**: Classes, inheritance, and encapsulation make it well-suited for building maintainable large-scale applications.\n3. **Rich Ecosystem**: Frameworks like Spring Boot, Hibernate, and Maven give you a mature, battle-tested toolchain.",
  "success": true
}

For handling errors gracefully in production, check out the Gemini API Error Handling Complete Guide.


Going Further: Production-Ready Enhancements

Add System Instructions

You can steer the AI's behavior by adding a systemInstruction field to your request body — useful for constraining the assistant to a specific domain or tone.

// Extend GeminiRequest to include systemInstruction
public record GeminiRequest(
    List<Content> contents,
    SystemInstruction systemInstruction
) {
    public record SystemInstruction(List<Part> parts) {}
    // ... (same structure as above)
}
 
// Usage in service
var systemInstruction = new GeminiRequest.SystemInstruction(
    List.of(new GeminiRequest.Part(
        "You are a Java expert. Answer questions concisely and accurately."
    ))
);

To master system instruction design for production applications, the Gemini 2.5 Pro System Instructions Mastery Guide covers advanced patterns in depth.

Token and Cost Control

Add generationConfig to your request to limit output tokens and manage costs. See the Gemini API Cost Optimization Guide for a full breakdown of cost-saving strategies.

Switching to Reactive

The block() call in the service makes this synchronous, which is fine for low-traffic scenarios. For high-throughput production services, return Mono<ChatResponse> from your service and Mono<ResponseEntity<ChatResponse>> from your controller. Spring MVC fully supports reactive return types starting from Spring Boot 3.x.


Looking back

In this guide, we built a working Gemini AI chat REST API using Spring Boot — without writing a single line of Python. The key takeaways are:

  • Use spring-boot-starter-webflux's WebClient for clean, non-blocking HTTP to the Gemini API
  • Externalize API keys and model names via application.properties for easy swapping
  • Java Records make for concise, immutable data models aligned with the API's JSON structure
  • Keeping Gemini logic in a dedicated service class makes future upgrades straightforward

From here, you can extend this into multi-turn conversations, integrate RAG (retrieval-augmented generation) for document Q&A, or connect Function Calling to external databases. The Spring Boot + Gemini combination is a practical choice for enterprise teams who want AI without leaving the Java ecosystem.

Share

Thank You for Reading

Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

API / SDK2026-04-02
Gemini API × Spring Boot Enterprise Production Guide: Spring AI, Multi-Tenancy, Security & Observability
A complete guide to running Gemini API in production with Spring Boot. Covers Spring AI framework integration, multi-tenant architecture, API key management, async processing, observability with Micrometer/OpenTelemetry, and enterprise testing strategies.
API / SDK2026-05-01
Speaker Diarization with Gemini API: Meetings and Podcasts
Use the Gemini API's multimodal audio understanding to label who said what in meeting recordings and podcasts — with a working Python example and prompt design tips.
API / SDK2026-04-11
Gemini 3.1 Pro REST API Getting Started Guide — generativelanguage.googleapis.com in Practice
A complete guide to calling the Gemini 3.1 Pro generativelanguage.googleapis.com REST API using curl, Python, and JavaScript — covering authentication, streaming, multi-turn chat, and common errors.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →