API Integration Guide
This comprehensive guide will show you how to integrate Nano Banana (Gemini 2.5 Flash Image) into your applications, whether you're building with Node.js, Python, or directly in the browser.
Installation
Node.js / JavaScript
npm install @google/generative-ai
# or
yarn add @google/generative-ai
# or
pnpm add @google/generative-ai
Python
pip install google-generativeai
TypeScript
For TypeScript projects, types are included with the package:
npm install @google/generative-ai
npm install -D @types/node
Basic Setup
Node.js / TypeScript
Create a client wrapper for better organization:
// lib/gemini-client.ts
import { GoogleGenerativeAI } from "@google/generative-ai";
if (!process.env.GOOGLE_GEMINI_API_KEY) {
throw new Error("GOOGLE_GEMINI_API_KEY is required");
}
export const genAI = new GoogleGenerativeAI(
process.env.GOOGLE_GEMINI_API_KEY
);
export function getImageModel() {
return genAI.getGenerativeModel({
model: "gemini-2.5-flash",
});
}
Python
# gemini_client.py
import google.generativeai as genai
import os
from typing import Optional
def configure_gemini(api_key: Optional[str] = None):
"""Configure the Gemini AI client"""
key = api_key or os.environ.get("GOOGLE_GEMINI_API_KEY")
if not key:
raise ValueError("API key is required")
genai.configure(api_key=key)
def get_image_model():
"""Get the image generation model"""
return genai.GenerativeModel('gemini-2.5-flash')
Image Generation
Simple Generation (Node.js)
import { getImageModel } from './lib/gemini-client';
async function generateImage(prompt: string) {
try {
const model = getImageModel();
const result = await model.generateContent({
contents: [{
role: "user",
parts: [{ text: `Generate an image: ${prompt}` }]
}]
});
const response = result.response;
return response;
} catch (error) {
console.error("Error generating image:", error);
throw error;
}
}
// Usage
const result = await generateImage(
"a modern minimalist logo for a tech startup"
);
Simple Generation (Python)
from gemini_client import configure_gemini, get_image_model
def generate_image(prompt: str):
"""Generate an image from a text prompt"""
try:
configure_gemini()
model = get_image_model()
response = model.generate_content(
f"Generate an image: {prompt}"
)
return response
except Exception as error:
print(f"Error generating image: {error}")
raise
# Usage
result = generate_image(
"a modern minimalist logo for a tech startup"
)
Advanced Features
Image Generation with Parameters
interface GenerateImageParams {
prompt: string;
aspectRatio?: string;
numberOfImages?: number;
style?: string;
}
async function generateImageAdvanced(params: GenerateImageParams) {
const {
prompt,
aspectRatio = "1:1",
numberOfImages = 1,
style
} = params;
const enhancedPrompt = [
prompt,
style && `Style: ${style}`,
aspectRatio !== "1:1" && `Aspect ratio: ${aspectRatio}`
]
.filter(Boolean)
.join(", ");
const model = getImageModel();
const result = await model.generateContent({
contents: [{
role: "user",
parts: [{ text: `Generate an image: ${enhancedPrompt}` }]
}]
});
return result.response;
}
// Usage
const result = await generateImageAdvanced({
prompt: "a sunset over mountains",
aspectRatio: "16:9",
numberOfImages: 2,
style: "professional photography"
});
Batch Image Generation
Generate multiple images efficiently:
async function batchGenerateImages(prompts: string[]) {
const results = await Promise.allSettled(
prompts.map(prompt =>
generateImage(prompt)
)
);
return results.map((result, index) => ({
prompt: prompts[index],
success: result.status === 'fulfilled',
data: result.status === 'fulfilled' ? result.value : null,
error: result.status === 'rejected' ? result.reason : null
}));
}
// Usage
const prompts = [
"a cat wearing sunglasses",
"a dog in a business suit",
"a hamster driving a tiny car"
];
const results = await batchGenerateImages(prompts);
results.forEach((result, i) => {
console.log(`Image ${i + 1}:`, result.success ? '✓' : '✗');
});
Error Handling
Comprehensive Error Handling
class GeminiError extends Error {
constructor(
message: string,
public code: string,
public statusCode?: number
) {
super(message);
this.name = 'GeminiError';
}
}
async function generateImageWithErrorHandling(prompt: string) {
try {
const model = getImageModel();
const result = await model.generateContent({
contents: [{
role: "user",
parts: [{ text: `Generate an image: ${prompt}` }]
}]
});
return {
success: true,
data: result.response
};
} catch (error: any) {
// Handle different error types
if (error.message?.includes('quota')) {
throw new GeminiError(
'API quota exceeded. Please try again later.',
'QUOTA_EXCEEDED',
429
);
}
if (error.message?.includes('authentication')) {
throw new GeminiError(
'Invalid API key',
'AUTHENTICATION_ERROR',
401
);
}
if (error.message?.includes('timeout')) {
throw new GeminiError(
'Request timed out',
'TIMEOUT',
408
);
}
// Generic error
throw new GeminiError(
error.message || 'Unknown error occurred',
'UNKNOWN_ERROR',
500
);
}
}
Python Error Handling
class GeminiError(Exception):
"""Custom exception for Gemini AI errors"""
def __init__(self, message: str, code: str, status_code: int = None):
self.message = message
self.code = code
self.status_code = status_code
super().__init__(self.message)
def generate_image_with_error_handling(prompt: str):
"""Generate image with comprehensive error handling"""
try:
model = get_image_model()
response = model.generate_content(f"Generate an image: {prompt}")
return {
"success": True,
"data": response
}
except Exception as error:
error_message = str(error)
if "quota" in error_message.lower():
raise GeminiError(
"API quota exceeded. Please try again later.",
"QUOTA_EXCEEDED",
429
)
if "authentication" in error_message.lower():
raise GeminiError(
"Invalid API key",
"AUTHENTICATION_ERROR",
401
)
raise GeminiError(
error_message or "Unknown error occurred",
"UNKNOWN_ERROR",
500
)
Rate Limiting
Implement client-side rate limiting to avoid hitting API limits:
class RateLimiter {
private queue: Array<() => Promise<any>> = [];
private processing = false;
private requestsPerMinute: number;
private requests: number[] = [];
constructor(requestsPerMinute: number = 60) {
this.requestsPerMinute = requestsPerMinute;
}
async execute<T>(fn: () => Promise<T>): Promise<T> {
return new Promise((resolve, reject) => {
this.queue.push(async () => {
try {
const result = await fn();
resolve(result);
} catch (error) {
reject(error);
}
});
this.process();
});
}
private async process() {
if (this.processing || this.queue.length === 0) return;
this.processing = true;
while (this.queue.length > 0) {
// Remove requests older than 1 minute
const now = Date.now();
this.requests = this.requests.filter(
time => now - time < 60000
);
// Wait if we've hit the limit
if (this.requests.length >= this.requestsPerMinute) {
const oldestRequest = this.requests[0];
const waitTime = 60000 - (now - oldestRequest);
await new Promise(resolve => setTimeout(resolve, waitTime));
continue;
}
// Process next request
const fn = this.queue.shift();
if (fn) {
this.requests.push(Date.now());
await fn();
}
}
this.processing = false;
}
}
// Usage
const rateLimiter = new RateLimiter(60); // 60 requests per minute
async function generateWithRateLimit(prompt: string) {
return rateLimiter.execute(() => generateImage(prompt));
}
Caching
Implement caching to reduce API calls and costs:
import { createHash } from 'crypto';
interface CacheEntry {
data: any;
timestamp: number;
}
class ImageCache {
private cache = new Map<string, CacheEntry>();
private ttl: number;
constructor(ttlMinutes: number = 60) {
this.ttl = ttlMinutes * 60 * 1000;
}
private generateKey(prompt: string, params?: any): string {
const data = JSON.stringify({ prompt, ...params });
return createHash('md5').update(data).digest('hex');
}
get(prompt: string, params?: any): any | null {
const key = this.generateKey(prompt, params);
const entry = this.cache.get(key);
if (!entry) return null;
// Check if expired
if (Date.now() - entry.timestamp > this.ttl) {
this.cache.delete(key);
return null;
}
return entry.data;
}
set(prompt: string, data: any, params?: any): void {
const key = this.generateKey(prompt, params);
this.cache.set(key, {
data,
timestamp: Date.now()
});
}
clear(): void {
this.cache.clear();
}
}
// Usage
const cache = new ImageCache(60); // Cache for 60 minutes
async function generateWithCache(prompt: string) {
// Check cache first
const cached = cache.get(prompt);
if (cached) {
console.log('Cache hit!');
return cached;
}
// Generate new image
const result = await generateImage(prompt);
// Store in cache
cache.set(prompt, result);
return result;
}
Next.js Integration
API Route
// app/api/generate/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { generateImage } from '@/lib/gemini-client';
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { prompt } = body;
if (!prompt) {
return NextResponse.json(
{ error: 'Prompt is required' },
{ status: 400 }
);
}
const result = await generateImage(prompt);
return NextResponse.json({
success: true,
data: result
});
} catch (error: any) {
return NextResponse.json(
{ error: error.message },
{ status: 500 }
);
}
}
Client-Side Hook
// hooks/use-image-generation.ts
import { useMutation } from '@tanstack/react-query';
interface GenerateParams {
prompt: string;
}
export function useImageGeneration() {
return useMutation({
mutationFn: async ({ prompt }: GenerateParams) => {
const response = await fetch('/api/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ prompt }),
});
if (!response.ok) {
throw new Error('Failed to generate image');
}
return response.json();
},
});
}
// Usage in component
function ImageGenerator() {
const mutation = useImageGeneration();
const handleGenerate = () => {
mutation.mutate({ prompt: 'a beautiful sunset' });
};
return (
<button onClick={handleGenerate} disabled={mutation.isPending}>
{mutation.isPending ? 'Generating...' : 'Generate'}
</button>
);
}
Express.js Integration
import express from 'express';
import { generateImage } from './gemini-client';
const app = express();
app.use(express.json());
app.post('/api/generate', async (req, res) => {
try {
const { prompt } = req.body;
if (!prompt) {
return res.status(400).json({
error: 'Prompt is required'
});
}
const result = await generateImage(prompt);
res.json({
success: true,
data: result
});
} catch (error: any) {
res.status(500).json({
error: error.message
});
}
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
Testing
Unit Tests (Jest)
import { generateImage } from './gemini-client';
describe('Image Generation', () => {
it('should generate an image from a prompt', async () => {
const prompt = 'a test image';
const result = await generateImage(prompt);
expect(result).toBeDefined();
expect(result.success).toBe(true);
});
it('should handle errors gracefully', async () => {
await expect(generateImage('')).rejects.toThrow();
});
});
Best Practices
- Environment Variables: Always use environment variables for API keys
- Error Handling: Implement comprehensive error handling
- Rate Limiting: Respect API rate limits
- Caching: Cache results when appropriate
- Logging: Log API calls for debugging and monitoring
- Validation: Validate input before making API calls
- Timeouts: Set appropriate timeout values
- Retries: Implement retry logic for transient failures
Security Considerations
// ❌ NEVER do this - exposing API key to client
const apiKey = "your_api_key"; // Sent to browser!
// ✅ DO this - keep API key on server
// Use environment variables and make calls from backend only
const apiKey = process.env.GOOGLE_GEMINI_API_KEY;
Input Sanitization
function sanitizePrompt(prompt: string): string {
// Remove excessive whitespace
prompt = prompt.trim().replace(/\s+/g, ' ');
// Limit length
if (prompt.length > 1000) {
prompt = prompt.substring(0, 1000);
}
return prompt;
}
Monitoring and Analytics
async function generateImageWithMetrics(prompt: string) {
const startTime = Date.now();
try {
const result = await generateImage(prompt);
const duration = Date.now() - startTime;
// Log success metrics
console.log({
event: 'image_generated',
duration,
promptLength: prompt.length,
success: true
});
return result;
} catch (error) {
const duration = Date.now() - startTime;
// Log error metrics
console.error({
event: 'image_generation_failed',
duration,
error: error.message,
success: false
});
throw error;
}
}
Next Steps
- Advanced Prompt Engineering - Master prompt writing
- Image Editing Guide - Learn to edit images
- Production Deployment - Deploy to production
Resources
You now have all the tools to integrate Nano Banana into your applications. Happy coding!