How to Use Next.js App Router API Routes for OpenMAIC Media Generation
OpenMAIC exposes video, image, and text-to-speech generation capabilities through standard Next.js App Router API routes in app/api/generate/*/route.ts, accepting POST requests with JSON payloads and returning uniform typed responses via apiSuccess and apiError helpers.
OpenMAIC implements its entire backend within the Next.js 13+ App Router architecture, leveraging serverless API route handlers to process media generation requests. Each endpoint follows a consistent six-step pipeline defined in feature-specific route.ts files under the app/api/generate/ directory, enabling seamless integration from frontend clients or external HTTP services. This guide covers the complete request lifecycle, provider resolution logic, and implementation details based on the THU-MAIC/OpenMAIC source code.
Core Route Architecture
Every OpenMAIC API route follows an identical architectural pattern implemented in app/api/generate/[feature]/route.ts files. The system processes requests through a standardized pipeline that ensures type safety, provider abstraction, and unified usage analytics.
Route Structure and Entry Points
Each media type exposes a dedicated endpoint that exports a maxDuration constant specifying the serverless timeout:
- Video generation:
app/api/generate/video/route.tsexportsmaxDuration = 300(5 minutes) - Image generation:
app/api/generate/image/route.tsexportsmaxDuration = 300(5 minutes) - Text-to-speech:
app/api/generate/tts/route.tsexportsmaxDuration = 30(30 seconds)
These duration values enforce hard limits on provider response times and inform frontend polling logic.
Request Processing Pipeline
Every incoming request flows through six distinct layers before returning a response:
- Request Reception: The handler receives a standard
NextRequestobject containing the JSON body and optional headers. - Validation: The system validates required fields (e.g.,
promptfor video/image,textfor TTS) and extracts optional provider-selection headers. - Provider Resolution: Using
lib/server/provider-config.ts, the route resolves the concrete provider ID, API key, base URL, and model ID viaresolveServerVideoProviderId,resolveVideoApiKey, andresolveVideoModel(or equivalent for other media types). - Media Generation: The route calls the specific generation function (
generateVideo,generateImage, orgenerateTTS) from the respective provider implementation files inlib/media/orlib/audio/. - Usage Recording: The system persists generation metrics via
recordGenerationUsageinlib/server/usage-storage.tsfor analytics tracking. - Response Formatting: The route returns a uniform JSON envelope using
apiSuccessorapiErrorhelpers fromlib/server/api-response.ts.
Calling the API from Client Applications
OpenMAIC routes accept standard HTTP POST requests with JSON payloads. Since the API runs under the same domain as the Next.js application, you use relative paths starting with /api/.
Base Client Implementation Pattern
Wrap your fetch calls in a reusable helper that handles the uniform response format:
async function callOpenMaicApi<T>(
path: string,
payload: unknown,
extraHeaders?: Record<string, string>
): Promise<T> {
const resp = await fetch(path, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...extraHeaders,
},
body: JSON.stringify(payload),
});
const data = await resp.json();
if (!data.success) {
throw new Error(`${data.errorCode}: ${data.error}`);
}
return data as T;
}
This helper enforces type safety and automatically throws on API errors using the errorCode and error fields from the standardized response envelope.
Video Generation Endpoint
The video generation route in app/api/generate/video/route.ts accepts prompts with optional duration, aspect ratio, and resolution parameters:
const videoResult = await callOpenMaicApi<{
result: {
url: string;
width: number;
height: number;
duration: number;
};
}>('/api/generate/video', {
prompt: 'A futuristic classroom with holographic screens',
duration: 30, // optional, in seconds
aspectRatio: '16:9', // optional
resolution: '1080p', // optional
}, {
// Optional headers to override default provider configuration
'x-video-provider': 'openai-video',
'x-video-model': 'gpt-4o-mini',
});
console.log('Video URL:', videoResult.result.url);
The handler resolves the specific video provider using resolveServerVideoProviderId, resolveVideoApiKey, and resolveVideoModel from lib/server/provider-config.ts before invoking generateVideo from lib/media/video-providers.ts.
Image Generation Endpoint
The image route in app/api/generate/image/route.ts supports dimension specifications via explicit width/height or aspect ratio mapping:
const imageResult = await callOpenMaicApi<{
result: {
url: string;
width: number;
height: number;
};
}>('/api/generate/image', {
prompt: 'A cute robot teaching math to children',
aspectRatio: '1:1', // Automatically maps to 1024×1024 via aspectRatioToDimensions()
}, {
'x-image-provider': 'stabilityai',
'x-image-model': 'sdxl-v2',
});
console.log('Image URL:', imageResult.result.url);
The route supports optional negativePrompt, width, height, and style parameters. Provider resolution follows the same pattern as video generation, utilizing lib/media/image-providers.ts and the shared configuration helpers.
Text-to-Speech Generation Endpoint
The TTS route in app/api/generate/tts/route.ts returns base64-encoded audio payloads:
const ttsResult = await callOpenMaicApi<{
audioId: string;
base64: string; // Base64-encoded audio data
format: string; // e.g., 'mp3'
}>('/api/generate/tts', {
text: 'Welcome to the AI classroom!',
audioId: 'welcome-001',
ttsProviderId: 'openai-tts',
ttsModelId: 'gpt-4o-mini',
ttsVoice: 'alloy',
ttsSpeed: 1.0, // optional
}, {
// Provide API key only if not using a managed provider
'x-api-key': 'sk-YOUR_OPENAI_KEY',
});
// Convert base64 to Uint8Array for audio playback
const audioBlob = Uint8Array.from(
atob(ttsResult.base64),
c => c.charCodeAt(0)
);
The TTS implementation in lib/audio/tts-providers.ts handles the concrete generation while the route manages provider resolution via the shared configuration system.
Provider Configuration and Request Headers
OpenMAIC uses a flexible provider resolution system that supports managed providers (pre-configured server-side) and per-request overrides via HTTP headers.
Available Configuration Headers
x-video-provider/x-image-provider: Override the default provider for the specific media type (e.g.,openai-video,runwayml-video,stabilityai).x-video-model/x-image-model/x-tts-model: Specify the exact model ID for the selected provider.x-api-key: Supply a per-request API key, used only when not utilizing a managed provider configuration.x-base-url: Override the provider's base URL for self-hosted instances (subject to SSRF protection guards inlib/server/provider-config.ts).
If you omit these headers, the system falls back to the first configured provider for that media type using the resolveServer*ProviderId helpers.
Managed vs. Unmanaged Providers
Managed providers have API keys and base URLs pre-configured in the server environment variables. For these, you omit the x-api-key header entirely. Unmanaged providers require you to pass the x-api-key header with each request, allowing client-side key rotation without server redeployment.
Error Handling and Response Format
All OpenMAIC API routes return a uniform JSON envelope regardless of success or failure:
{
"success": false,
"errorCode": "MISSING_REQUIRED_FIELD",
"error": "Missing prompt"
}
Response Structure
success: Boolean indicating request status.result: Present only whensuccessis true; contains the generated media data.errorCode: Machine-readable error identifier fromAPI_ERROR_CODESdefined inlib/server/api-response.ts.error: Human-readable description of the failure.
Common Error Codes
Client applications should inspect errorCode to implement specific recovery logic:
MISSING_REQUIRED_FIELD: The request body lacks a mandatory parameter (e.g., missingprompt).PROVIDER_RESOLUTION_FAILED: The system could not resolve a valid provider from headers or configuration.GENERATION_FAILED: The underlying provider returned an error during media generation.USAGE_LIMIT_EXCEEDED: The request exceeds configured rate limits or quotas recorded inlib/server/usage-storage.ts.
Summary
- Route Location: All media generation endpoints reside in
app/api/generate/[feature]/route.tsfiles within the Next.js App Router structure. - Standardized Flow: Every route implements a six-step pipeline: request reception, validation, provider resolution, media generation, usage recording via
recordGenerationUsage, and response formatting viaapiSuccess/apiError. - Provider Flexibility: Use headers (
x-video-provider,x-image-provider,x-api-key) to override default providers or pass client-specific credentials without redeploying the server. - Uniform Responses: All endpoints return JSON envelopes containing boolean
successstatus and either aresultobject orerrorCode/errorfields. - Type Safety: Implementation in TypeScript ensures typed request bodies and responses, with concrete provider implementations in
lib/media/video-providers.ts,lib/media/image-providers.ts, andlib/audio/tts-providers.ts.
Frequently Asked Questions
How do I specify a different AI provider for a single request?
Pass the appropriate provider header (x-video-provider, x-image-provider, or x-tts-provider) with your POST request. For example, set x-video-provider: runwayml-video to use RunwayML instead of the default OpenAI video model. The server resolves the provider configuration using lib/server/provider-config.ts and falls back to the first configured server-side provider if headers are omitted.
What is the maximum duration for API requests, and how is it enforced?
Each route exports a maxDuration constant (typically 300 seconds for video/image generation and 30 seconds for TTS). This value defines the serverless function timeout enforced by the hosting platform and is consumed by frontend polling logic to manage user expectations for long-running generations.
Can I use these API routes from an external application or only from the Next.js frontend?
You can call these routes from any HTTP client. While the examples use relative paths (/api/generate/video), external applications should use the full deployment URL. Authentication works via the x-api-key header for unmanaged providers, or via pre-configured managed providers that do not require client-side keys.
How does error handling work when a provider API fails?
The route catches provider-specific errors in the generation step and returns a standardized error envelope with success: false, an errorCode from the API_ERROR_CODES enumeration in lib/server/api-response.ts, and a descriptive error message. Client code should verify data.success before accessing data.result, and implement branch logic based on specific errorCode values to handle retries or user notifications appropriately.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →