Language-Aware API Server
StatusFlow includes built-in support for bilingual responses (Spanish and English). This example shows how to build an API that automatically responds in the user’s preferred language.Complete Bilingual Server
import express, { Request, Response, NextFunction } from 'express';
import {
StatusFlow,
StatusFlowCodes,
StatusFlowLang,
statusFlowMiddleware
} from 'status-flow';
const app = express();
app.use(express.json());
// Language detection middleware
const detectLanguage = (req: Request, res: Response, next: NextFunction) => {
// Priority 1: x-lang header
let lang = req.headers['x-lang'] as StatusFlowLang;
// Priority 2: Accept-Language header
if (!lang) {
const acceptLanguage = req.headers['accept-language'];
if (acceptLanguage) {
lang = acceptLanguage.startsWith('en') ? 'en' : 'es';
}
}
// Priority 3: Query parameter
if (!lang && req.query.lang) {
lang = req.query.lang as StatusFlowLang;
}
// Default to Spanish
(req as any).lang = (lang === 'en' || lang === 'es') ? lang : 'es';
next();
};
app.use(detectLanguage);
// Helper to get language from request
const getLang = (req: Request): StatusFlowLang => {
return (req as any).lang || 'es';
};
// Mock data store
interface Product {
id: string;
name: { en: string; es: string };
description: { en: string; es: string };
price: number;
stock: number;
}
const products: Product[] = [
{
id: '1',
name: { en: 'Laptop', es: 'Portátil' },
description: {
en: 'High-performance laptop',
es: 'Portátil de alto rendimiento'
},
price: 999.99,
stock: 10
},
{
id: '2',
name: { en: 'Mouse', es: 'Ratón' },
description: {
en: 'Wireless ergonomic mouse',
es: 'Ratón inalámbrico ergonómico'
},
price: 29.99,
stock: 50
},
{
id: '3',
name: { en: 'Keyboard', es: 'Teclado' },
description: {
en: 'Mechanical gaming keyboard',
es: 'Teclado mecánico para gaming'
},
price: 149.99,
stock: 0
}
];
// Localized messages
const messages = {
productNotFound: {
en: 'Product not found',
es: 'Producto no encontrado'
},
outOfStock: {
en: 'Product is out of stock',
es: 'Producto sin stock'
},
invalidQuantity: {
en: 'Invalid quantity requested',
es: 'Cantidad solicitada inválida'
},
insufficientStock: {
en: 'Insufficient stock available',
es: 'Stock insuficiente disponible'
},
orderCreated: {
en: 'Order created successfully',
es: 'Pedido creado exitosamente'
}
};
// API Routes
// Welcome endpoint with language detection
app.get('/api/welcome', (req, res) => {
const lang = getLang(req);
res.json(
StatusFlow({
code: StatusFlowCodes.OK,
lang,
extra: {
greeting: lang === 'en'
? 'Welcome to our Bilingual API!'
: '¡Bienvenido a nuestra API Bilingüe!',
detectedLanguage: lang,
supportedLanguages: ['en', 'es']
}
})
);
});
// Get all products (with localized content)
app.get('/api/products', (req, res) => {
const lang = getLang(req);
const localizedProducts = products.map(p => ({
id: p.id,
name: p.name[lang],
description: p.description[lang],
price: p.price,
stock: p.stock,
available: p.stock > 0
}));
res.json(
StatusFlow({
code: StatusFlowCodes.OK,
lang,
extra: {
products: localizedProducts,
total: localizedProducts.length
}
})
);
});
// Get single product
app.get('/api/products/:id', (req, res, next) => {
const lang = getLang(req);
const product = products.find(p => p.id === req.params.id);
if (!product) {
return next({
code: StatusFlowCodes.NOT_FOUND,
message: messages.productNotFound[lang],
extra: { productId: req.params.id }
});
}
res.json(
StatusFlow({
code: StatusFlowCodes.OK,
lang,
extra: {
product: {
id: product.id,
name: product.name[lang],
description: product.description[lang],
price: product.price,
stock: product.stock,
available: product.stock > 0
}
}
})
);
});
// Create order (with validation and localized errors)
app.post('/api/orders', (req, res, next) => {
const lang = getLang(req);
const { productId, quantity } = req.body;
// Validate quantity
if (!quantity || quantity < 1) {
return next({
code: StatusFlowCodes.BAD_REQUEST,
message: messages.invalidQuantity[lang],
extra: { field: 'quantity', provided: quantity }
});
}
// Find product
const product = products.find(p => p.id === productId);
if (!product) {
return next({
code: StatusFlowCodes.NOT_FOUND,
message: messages.productNotFound[lang],
extra: { productId }
});
}
// Check stock
if (product.stock === 0) {
return next({
code: StatusFlowCodes.CONFLICT,
message: messages.outOfStock[lang],
extra: {
productId,
productName: product.name[lang]
}
});
}
if (product.stock < quantity) {
return next({
code: StatusFlowCodes.CONFLICT,
message: messages.insufficientStock[lang],
extra: {
requested: quantity,
available: product.stock,
productName: product.name[lang]
}
});
}
// Create order
const order = {
id: `order-${Date.now()}`,
productId,
productName: product.name[lang],
quantity,
unitPrice: product.price,
total: product.price * quantity,
createdAt: new Date().toISOString()
};
// Update stock (in real app, this would be in a transaction)
product.stock -= quantity;
res.status(201).json(
StatusFlow({
code: StatusFlowCodes.CREATED,
lang,
overrideMessage: messages.orderCreated[lang],
extra: { order }
})
);
});
// Search products
app.get('/api/search', (req, res, next) => {
const lang = getLang(req);
const query = (req.query.q as string)?.toLowerCase();
if (!query) {
return next({
code: StatusFlowCodes.BAD_REQUEST,
message: lang === 'en'
? 'Search query is required'
: 'Se requiere una consulta de búsqueda',
extra: { parameter: 'q' }
});
}
const results = products.filter(p => {
const name = p.name[lang].toLowerCase();
const desc = p.description[lang].toLowerCase();
return name.includes(query) || desc.includes(query);
}).map(p => ({
id: p.id,
name: p.name[lang],
description: p.description[lang],
price: p.price,
stock: p.stock
}));
res.json(
StatusFlow({
code: StatusFlowCodes.OK,
lang,
extra: {
query,
results,
count: results.length
}
})
);
});
// Language preference endpoint
app.get('/api/language-info', (req, res) => {
const lang = getLang(req);
res.json({
currentLanguage: lang,
detectionMethod: req.headers['x-lang'] ? 'x-lang header'
: req.query.lang ? 'query parameter'
: req.headers['accept-language'] ? 'Accept-Language header'
: 'default',
supportedLanguages: [
{ code: 'en', name: 'English' },
{ code: 'es', name: 'Español' }
],
examples: {
header: 'x-lang: en',
query: '?lang=es',
acceptLanguage: 'Accept-Language: en-US,en;q=0.9'
}
});
});
// Enhanced statusFlowMiddleware with language detection
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
const lang = getLang(req);
if (err && typeof err.code === 'number') {
const response = StatusFlow({
code: err.code,
lang,
extra: err.extra || {},
overrideMessage: err.message || undefined,
});
res.status(err.code).json(response);
} else {
const response = StatusFlow({
code: 500,
lang,
overrideMessage: lang === 'en'
? 'Internal server error'
: 'Error interno del servidor',
extra: { originalError: err },
});
res.status(500).json(response);
}
});
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Bilingual API running on http://localhost:${PORT}`);
console.log('Supported languages: English (en), Spanish (es)');
console.log('\nTry these examples:');
console.log(' English: curl -H "x-lang: en" http://localhost:3000/api/welcome');
console.log(' Spanish: curl -H "x-lang: es" http://localhost:3000/api/welcome');
});
export default app;
Testing Language Detection
Example Responses
English Response (200)
English Response (200)
{
"success": true,
"message": "HTTP 200 - OK",
"code": 200,
"greeting": "Welcome to our Bilingual API!",
"detectedLanguage": "en",
"supportedLanguages": ["en", "es"],
"info": {
"name": "OK",
"category": "Successful Response",
"description": "The request was successful",
"possibleCauses": [
"Operation completed successfully"
]
}
}
Spanish Response (200)
Spanish Response (200)
{
"success": true,
"message": "HTTP 200 - Correcto",
"code": 200,
"greeting": "¡Bienvenido a nuestra API Bilingüe!",
"detectedLanguage": "es",
"supportedLanguages": ["en", "es"],
"info": {
"name": "Correcto",
"category": "Respuesta exitosa",
"description": "La solicitud se completó exitosamente",
"possibleCauses": [
"Operación completada correctamente"
]
}
}
English Error (404)
English Error (404)
{
"success": false,
"message": "Product not found",
"code": 404,
"productId": "999",
"info": {
"name": "Not Found",
"category": "Client Error",
"description": "The requested resource does not exist",
"possibleCauses": [
"Incorrect URL",
"Resource deleted or moved"
]
}
}
Spanish Error (404)
Spanish Error (404)
{
"success": false,
"message": "Producto no encontrado",
"code": 404,
"productId": "999",
"info": {
"name": "No encontrado",
"category": "Error del cliente",
"description": "El recurso solicitado no existe",
"possibleCauses": [
"URL incorrecta",
"Recurso eliminado o movido"
]
}
}
Language Detection Strategy
StatusFlow detects the preferred language using this priority order:x-lang Header (Highest Priority)
curl -H "x-lang: en" http://localhost:3000/api/welcome
Accept-Language Header
curl -H "Accept-Language: en-US,en;q=0.9" http://localhost:3000/api/welcome
Query Parameter
curl "http://localhost:3000/api/welcome?lang=en"
Best Practices for Bilingual APIs
Centralize Localized Messages
Centralize Localized Messages
Store all user-facing messages in a centralized object:
const messages = {
productNotFound: {
en: 'Product not found',
es: 'Producto no encontrado'
},
// ... more messages
};
// Use throughout your app
return next({
code: StatusFlowCodes.NOT_FOUND,
message: messages.productNotFound[lang]
});
Localize Data Content
Localize Data Content
Store multilingual content in your database:
interface Product {
id: string;
name: { en: string; es: string };
description: { en: string; es: string };
// ... other fields
}
// Return localized version
const localizedProduct = {
id: product.id,
name: product.name[lang],
description: product.description[lang]
};
Consistent Language Detection
Consistent Language Detection
Use middleware to detect language once:
app.use((req, res, next) => {
const lang = req.headers['x-lang']
|| detectFromAcceptLanguage(req)
|| 'es';
(req as any).lang = lang;
next();
});
Document Language Support
Document Language Support
Provide an endpoint that explains language options:
app.get('/api/language-info', (req, res) => {
res.json({
supportedLanguages: ['en', 'es'],
detectionMethod: 'x-lang header, Accept-Language, or query param',
examples: {
header: 'x-lang: en',
query: '?lang=es'
}
});
});
Language Middleware Pattern
Create reusable middleware to detect and attach language to requests:
import { Request, Response, NextFunction } from 'express';
import { StatusFlowLang } from 'status-flow';
export const detectLanguage = (req: Request, res: Response, next: NextFunction) => {
let lang: StatusFlowLang = 'es'; // default
// Check x-lang header
const xLang = req.headers['x-lang'];
if (xLang === 'en' || xLang === 'es') {
lang = xLang;
}
// Check Accept-Language
else if (req.headers['accept-language']?.startsWith('en')) {
lang = 'en';
}
// Check query parameter
else if (req.query.lang === 'en' || req.query.lang === 'es') {
lang = req.query.lang as StatusFlowLang;
}
(req as any).lang = lang;
next();
};
export const getLang = (req: Request): StatusFlowLang => {
return (req as any).lang || 'es';
};
Integration with Frontend
// Detect browser language
const userLang = navigator.language.startsWith('es') ? 'es' : 'en';
fetch('http://localhost:3000/api/products', {
headers: {
'x-lang': userLang
}
})
.then(res => res.json())
.then(data => console.log(data));
Key Features
Automatic Detection
StatusFlow automatically detects language from headers, query params, or defaults.
Rich Metadata
All responses include localized info with name, category, description, and causes.
Flexible Integration
Works with any language detection strategy - headers, cookies, JWT, database.
Complete Coverage
All HTTP status codes have translations in both English and Spanish.
Next Steps
Basic Usage
Learn the fundamentals of StatusFlow
Advanced Integration
Explore advanced patterns and production practices