Response Format

Last Updated: May 19, 2025

This document describes the standardized API response format used in the Typus Development Framework.

Overview

The Typus Development Framework implements a consistent response format for all API endpoints. This standardization ensures that frontend clients can reliably parse and handle responses, simplifying error handling and data extraction.

The response format is automatically applied by the RouterHelper, which wraps all controller methods and ensures that responses follow the standard structure regardless of the specific controller implementation.

Standard Response Structure

All API responses follow a consistent JSON structure:

{
  "status": 200,
  "data": { ... },
  "error": null
}

For error responses:

{
  "status": 400,
  "data": null,
  "error": {
    "message": "Error message",
    "code": "ERROR_CODE"
  }
}

This structure provides a clear separation between successful responses and errors, making it easy for clients to determine the outcome of a request and handle it appropriately.

Response Processing Flow

The RouterHelper automatically processes controller responses and formats them according to the standard structure:

Direct Return

Pre-formatted

Error Thrown

Controller Method

Result Type?

Wrap in Standard Format

Use As Is

Format Error Response

Send Response

Success Responses

When a controller method returns data successfully, the RouterHelper automatically wraps it in the standard response format:

  1. The HTTP status code is set to 200 (or the specified status code)
  2. The response body includes the status, data, and a null error field
  3. The content type is set to application/json

Example of a successful response:

{
  "status": 200,
  "data": {
    "id": 123,
    "name": "Example Item",
    "createdAt": "2025-05-19T10:30:00Z"
  },
  "error": null
}

Error Responses

When an error occurs, either thrown by a controller method or caught by the RouterHelper, it is formatted into a standardized error response:

  1. The HTTP status code is set based on the error type (e.g., 400, 401, 403, 404, 500)
  2. The response body includes the status, a null data field, and an error object
  3. The error object contains a message and a code

Example of an error response:

{
  "status": 404,
  "data": null,
  "error": {
    "message": "User with ID 123 not found",
    "code": "NOT_FOUND"
  }
}

Error Types and Status Codes

The framework provides several built-in error types that map to specific HTTP status codes:

Error Type Status Code Error Code Description
BadRequestError 400 BAD_REQUEST Invalid request parameters or body
ValidationError 400 VALIDATION_ERROR Request validation failed
UnauthorizedError 401 UNAUTHORIZED Authentication required
ForbiddenError 403 FORBIDDEN Insufficient permissions
NotFoundError 404 NOT_FOUND Resource not found
BaseError 500 INTERNAL_ERROR Generic server error

Validation Errors

Validation errors include additional information about the specific validation failures:

{
  "status": 400,
  "data": null,
  "error": {
    "message": "Validation failed",
    "code": "VALIDATION_ERROR",
    "errors": [
      {
        "path": ["body", "email"],
        "message": "Invalid email format"
      },
      {
        "path": ["body", "password"],
        "message": "Password must be at least 8 characters"
      }
    ]
  }
}

Implementation Details

RouterHelper

The RouterHelper is responsible for:

  1. Wrapping controller methods in a safe handler that catches and processes errors
  2. Standardizing response formats by ensuring all responses follow the defined structure
  3. Setting appropriate status codes based on the response or error type
  4. Logging request and response details for debugging and monitoring

The RouterHelper's safeHandler function processes the result of controller methods:

  • If the result is already in the standard format, it is used as is
  • If the result is a direct return value, it is wrapped in the standard format
  • If an error is thrown, it is converted to the standard error format

BaseController

The BaseController provides helper methods for creating standardized responses:

  • success(res, data, status) - Creates a success response
  • error(res, message, code, status) - Creates an error response
  • notFound(res, message) - Creates a 404 not found response
  • badRequest(res, message, code) - Creates a 400 bad request response
  • unauthorized(res, message, code) - Creates a 401 unauthorized response
  • forbidden(res, message, code) - Creates a 403 forbidden response

These methods ensure consistent response formatting across all controllers.

Error Handling

The framework uses a centralized error handling approach:

  1. Typed errors are thrown by services when exceptional conditions occur
  2. Controller methods can catch and handle specific errors
  3. RouterHelper catches any uncaught errors and formats them appropriately
  4. Global error handler catches any errors that escape the RouterHelper

This multi-layered approach ensures that all errors are properly formatted and returned to the client.

Best Practices

Throwing Errors

Services should throw typed errors instead of returning error objects:

// Instead of:
return { error: { message: 'Not found', code: 'NOT_FOUND', status: 404 } };

// Use:
throw new NotFoundError('Resource not found');

Returning Data

Services should return data directly without wrapping in a data object:

// Instead of:
return { data: result };

// Use:
return result;

Controller Methods

Controller methods should focus on:

  1. Extracting and validating request parameters
  2. Calling service methods to perform business logic
  3. Returning the result directly (let RouterHelper handle formatting)
  4. Catching specific errors only when special handling is needed

Example controller method:

async getUser(req: Request, res: Response) {
  const { id } = req.params;
  return await this.userService.getUserById(id);
}

Error Context

When throwing errors, include relevant context to help with debugging:

throw new NotFoundError(`User with ID ${id} not found`, { userId: id });

Client Integration

Frontend clients should be designed to handle the standard response format:

  1. Check the status code to determine if the request was successful
  2. Extract data from the data field for successful responses
  3. Handle errors based on the error code and message
  4. Display appropriate messages to the user based on the error type

Example client-side handling:

async function fetchData(url) {
  try {
    const response = await fetch(url);
    const result = await response.json();
    
    if (result.error) {
      // Handle error based on code
      switch (result.error.code) {
        case 'NOT_FOUND':
          // Handle not found
          break;
        case 'UNAUTHORIZED':
          // Handle unauthorized
          break;
        default:
          // Handle other errors
      }
      throw new Error(result.error.message);
    }
    
    return result.data;
  } catch (error) {
    // Handle fetch or parsing errors
    console.error('Error fetching data:', error);
    throw error;
  }
}

Conclusion

The standardized response format in the Typus Development Framework provides a consistent and predictable API interface. By automatically formatting all responses through the RouterHelper, the framework ensures that clients can rely on a uniform structure for both successful responses and errors.

This approach simplifies client-side integration, improves error handling, and makes the API more maintainable and user-friendly.

WARNING

Failed to fetch dynamically imported module: https://typus.dev/assets/RecursiveNavItem-Cep7andh.js

{ "stack": "AppError: Failed to fetch dynamically imported module: https://typus.dev/assets/RecursiveNavItem-Cep7andh.js\n at https://typus.dev/assets/index-DS79FI73.js:315:420\n at dn (https://typus.dev/assets/vue-vendor-Ct83yDeK.js:13:1385)\n at We (https://typus.dev/assets/vue-vendor-Ct83yDeK.js:13:1455)\n at Ws.t.__weh.t.__weh (https://typus.dev/assets/vue-vendor-Ct83yDeK.js:14:7364)\n at jt (https://typus.dev/assets/vue-vendor-Ct83yDeK.js:13:1866)\n at v (https://typus.dev/assets/vue-vendor-Ct83yDeK.js:14:4019)\n at https://typus.dev/assets/vue-vendor-Ct83yDeK.js:14:4097" }