Skip to main content
This guide will walk you through creating a complete Express API using StatusFlow, from initial setup to testing endpoints with both error handling approaches.

What We’ll Build

We’ll create a simple user management API with:
  • User retrieval endpoint
  • Error handling for missing users
  • Bilingual response support
  • Both exception-based and functional error handling patterns

Method 1: StatusFlow Function Approach

This approach uses the StatusFlow function to generate responses and the statusFlowMiddleware for error handling.
1

Create your project

mkdir my-statusflow-api
cd my-statusflow-api
npm init -y
npm install express status-flow
For TypeScript projects:
npm install --save-dev typescript @types/express @types/node ts-node
npx tsc --init
2

Create server.js (or server.ts)

Create a file called server.js with the following code:
import express, { Request, Response, NextFunction } from 'express';
import { StatusFlow, StatusFlowCodes, statusFlowMiddleware } from 'status-flow';

const app = express();
app.use(express.json());

// Mock database
const users = [
  { id: 1, name: 'Alice', email: '[email protected]' },
  { id: 2, name: 'Bob', email: '[email protected]' },
];

// Success endpoint with StatusFlow
app.get('/api/users', (req: Request, res: Response) => {
  const lang = req.headers['x-lang'] === 'en' ? 'en' : 'es';
  res.json(
    StatusFlow({ 
      code: StatusFlowCodes.OK,
      lang,
      extra: { users }
    })
  );
});

// Get user by ID
app.get('/api/users/:id', (req: Request, res: Response) => {
  const userId = parseInt(req.params.id);
  const user = users.find(u => u.id === userId);
  const lang = req.headers['x-lang'] === 'en' ? 'en' : 'es';
  
  if (!user) {
    return res.status(404).json(
      StatusFlow({ 
        code: StatusFlowCodes.NOT_FOUND,
        lang,
        overrideMessage: lang === 'en' ? 'User not found' : 'Usuario no encontrado',
        extra: { requestedId: userId }
      })
    );
  }
  
  res.json(
    StatusFlow({ 
      code: StatusFlowCodes.OK,
      lang,
      extra: { user }
    })
  );
});

// Error endpoint using next() for middleware handling
app.get('/api/error', (req: Request, res: Response, next: NextFunction) => {
  // Pass error object with code to middleware
  next({
    code: StatusFlowCodes.INTERNAL_SERVER_ERROR,
    message: 'Something went wrong!',
    extra: { timestamp: new Date().toISOString() }
  });
});

// Register StatusFlow middleware (must be after routes)
app.use(statusFlowMiddleware);

const PORT = 3000;
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});
The statusFlowMiddleware intercepts errors passed to next() and automatically generates StatusFlow responses based on the error’s code property.
3

Start the server

npx ts-node server.ts
You should see:
Server running on http://localhost:3000
4

Test the endpoints

Test the API using curl or your favorite HTTP client:
# Get all users (English)
curl -H "x-lang: en" http://localhost:3000/api/users

# Get user by ID (Spanish - default)
curl http://localhost:3000/api/users/1

# Test 404 error (English)
curl -H "x-lang: en" http://localhost:3000/api/users/999

# Test error middleware
curl http://localhost:3000/api/error
Success Response (English):
{
  "success": true,
  "message": "HTTP 200 - OK",
  "code": 200,
  "users": [
    { "id": 1, "name": "Alice", "email": "[email protected]" },
    { "id": 2, "name": "Bob", "email": "[email protected]" }
  ],
  "info": {
    "name": "OK",
    "category": "Successful",
    "description": "Standard HTTP success code 200.",
    "possibleCauses": ["General cause depending on context."]
  }
}
404 Response (Spanish):
{
  "success": false,
  "message": "Usuario no encontrado",
  "code": 404,
  "requestedId": 999,
  "info": {
    "name": "No Encontrado",
    "category": "Error del Cliente",
    "description": "El recurso solicitado no se pudo encontrar.",
    "possibleCauses": [
      "El recurso no existe",
      "URL o identificador incorrecto"
    ]
  }
}

Method 2: Exception-Based Approach

This approach uses exception classes and the httpErrorMiddleware for a more traditional error handling pattern.
1

Create server with exceptions

Create a file called server-exceptions.js:
import express, { Request, Response, NextFunction } from 'express';
import { 
  NotFoundException, 
  BadRequestException,
  httpErrorMiddleware,
  createSuccessResponse 
} from 'status-flow';

const app = express();
app.use(express.json());

// Mock database
const users = [
  { id: 1, name: 'Alice', email: '[email protected]' },
  { id: 2, name: 'Bob', email: '[email protected]' },
];

// Get all users
app.get('/api/users', (req: Request, res: Response) => {
  res.json(createSuccessResponse({ users }, 'Users retrieved successfully'));
});

// Get user by ID - throws exception if not found
app.get('/api/users/:id', (req: Request, res: Response, next: NextFunction) => {
  try {
    const userId = parseInt(req.params.id);
    
    if (isNaN(userId)) {
      throw new BadRequestException('Invalid user ID format');
    }
    
    const user = users.find(u => u.id === userId);
    
    if (!user) {
      throw new NotFoundException(`User with ID ${userId} not found`);
    }
    
    res.json(createSuccessResponse({ user }, 'User found'));
  } catch (error) {
    next(error);
  }
});

// Create user endpoint
app.post('/api/users', (req: Request, res: Response, next: NextFunction) => {
  try {
    const { name, email } = req.body;
    
    if (!name || !email) {
      throw new BadRequestException(
        'Name and email are required',
        { missingFields: { name: !name, email: !email } }
      );
    }
    
    const newUser = {
      id: users.length + 1,
      name,
      email
    };
    
    users.push(newUser);
    res.status(201).json(createSuccessResponse({ user: newUser }, 'User created', 201));
  } catch (error) {
    next(error);
  }
});

// Register error middleware (must be after routes)
app.use(httpErrorMiddleware);

const PORT = 3001;
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});
2

Test exception handling

# Test 404 error
curl http://localhost:3001/api/users/999

# Test 400 error with invalid ID
curl http://localhost:3001/api/users/abc

# Test 400 error with missing fields
curl -X POST http://localhost:3001/api/users \
  -H "Content-Type: application/json" \
  -d '{"name": "Charlie"}'

# Test successful creation
curl -X POST http://localhost:3001/api/users \
  -H "Content-Type: application/json" \
  -d '{"name": "Charlie", "email": "[email protected]"}'
404 Exception Response:
{
  "status": 404,
  "message": "User with ID 999 not found"
}
400 Exception Response:
{
  "status": 400,
  "message": "Name and email are required",
  "details": {
    "missingFields": {
      "name": false,
      "email": true
    }
  }
}

Understanding StatusFlow Components

StatusFlow Function

The StatusFlow function generates rich response objects:
StatusFlow({
  code: 404,                    // HTTP status code (required)
  lang: 'en',                   // Language: 'en' or 'es' (default: 'es')
  overrideMessage: 'Custom',    // Override default message (optional)
  extra: { userId: 123 }        // Additional data to include (optional)
})

StatusFlowCodes

Use constants instead of magic numbers:
StatusFlowCodes.OK                          // 200
StatusFlowCodes.CREATED                     // 201
StatusFlowCodes.BAD_REQUEST                 // 400
StatusFlowCodes.UNAUTHORIZED                // 401
StatusFlowCodes.FORBIDDEN                   // 403
StatusFlowCodes.NOT_FOUND                   // 404
StatusFlowCodes.CONFLICT                    // 409
StatusFlowCodes.INTERNAL_SERVER_ERROR       // 500

Exception Classes

All exception classes accept:
new NotFoundException(
  'Resource not found',           // message (optional)
  { resourceId: 123 }            // details (optional)
)
Available exceptions:
  • BadRequestException (400)
  • UnauthorizedException (401)
  • ForbiddenException (403)
  • NotFoundException (404)
  • ConflictException (409)
  • InternalServerErrorException (500)

Key Differences Between Approaches

FeatureStatusFlow FunctionException Classes
MiddlewarestatusFlowMiddlewarehttpErrorMiddleware
Response FormatRich with info objectSimple status, message, details
Bilingual SupportBuilt-in with lang parameterNot included
Error HandlingPass object to next() with codeThrow exception classes
Success ResponsesUse StatusFlow() with 2xx codesUse createSuccessResponse()
Use CaseBilingual APIs, rich error contextTraditional REST APIs
You can mix both approaches in the same application, but use only one middleware at a time to avoid conflicts.

Best Practices

  1. Choose one approach - Pick either StatusFlow function or exceptions for consistency
  2. Language detection - Read from headers (x-lang) or user preferences
  3. Use StatusFlowCodes - Avoid magic numbers for status codes
  4. Add context with extra - Include relevant data in error responses
  5. Register middleware last - Error middleware must come after all routes
  6. Wrap async routes - Use try/catch in async route handlers

Next Steps

Now that you have a working StatusFlow API, explore:

API Reference

Detailed documentation of all functions and classes

Examples

More advanced usage patterns and real-world examples

Build docs developers (and LLMs) love