Skip to content

07 Response Formatting

DodaTech 4 min read

title: Response Formatting in Node.js REST APIs weight: 17 date: 2026-06-28 lastmod: 2026-06-28 description: Learn response formatting in Node.js REST APIs including JSON responses, status codes, headers, response envelopes, and formatting helpers for consistent output. tags: [api-development, nodejs]


Response formatting in Node.js REST APIs standardizes how data is returned to clients using consistent JSON structures, proper status codes, response headers, and envelope patterns for success, error, and paginated responses.

```mermaid
flowchart TD
  A[Response] --> B[Status Code]
  A --> C[Headers]
  A --> D[Body Format]
  B --> E[200, 201, 204, 400, 404, 500]
  C --> F[Content-Type, Cache-Control]
  D --> G[Success Envelope]
  D --> H[Error Envelope]
  D --> I[Pagination Envelope]
  style A fill:#e1f5fe
  style B fill:#fff9c4
  style G fill:#c8e6c9
  style H fill:#ffcdd2

A response formatting strategy defines how every response looks. Success responses include status, data, and optional metadata. Error responses include status, error code, message, and details. Paginated responses include items, total, page, and links.

Think of response formatting like packaging. No matter what you are shipping (data), the box should look the same: same label position (structure), same tape (status code), and same handling instructions (headers).

Example: Response Helper Functions

class ApiResponse {
  static success(res, data, statusCode = 200) {
    return res.status(statusCode).json({
      status: 'success',
      data
    });
  }

  static created(res, data) {
    return res.status(201).json({
      status: 'success',
      data
    });
  }

  static paginated(res, items, total, page, perPage) {
    return res.status(200).json({
      status: 'success',
      data: items,
      meta: {
        total,
        page,
        perPage,
        totalPages: Math.ceil(total / perPage)
      }
    });
  }

  static error(res, message, statusCode = 500, code = 'INTERNAL_ERROR') {
    return res.status(statusCode).json({
      status: 'error',
      error: { code, message }
    });
  }
}

// Usage in controller
app.get('/api/users/:id', (req, res) => {
  const user = findUser(req.params.id);
  if (!user) {
    return ApiResponse.error(res, 'User not found', 404, 'NOT_FOUND');
  }
  return ApiResponse.success(res, user);
});

Expected output:

{"status": "success", "data": {"id": 42, "name": "Alice"}}

Example: Setting Response Headers

app.get('/api/users', (req, res) => {
  const users = getAllUsers();

  // Custom headers
  res.setHeader('X-API-Version', '1.0');
  res.setHeader('X-Total-Count', users.length);
  res.setHeader('Cache-Control', 'public, max-age=300');

  // Link header for pagination
  const links = [
    `<https://api.example.com/users?page=1>; rel="first"`,
    `<https://api.example.com/users?page=3>; rel="next"`
  ];
  res.setHeader('Link', links.join(', '));

  res.json({ status: 'success', data: users });
});

Example: Content Negotiation for Response Format

app.get('/api/users/:id', (req, res) => {
  const user = findUser(req.params.id);

  // Based on Accept header
  if (req.accepts('xml')) {
    // Return XML
    res.setHeader('Content-Type', 'application/xml');
    res.send(`<user><id>${user.id}</id><name>${user.name}</name></user>`);
  } else if (req.accepts('json')) {
    // Default to JSON
    res.json({ status: 'success', data: user });
  } else {
    res.status(406).json({ status: 'error', error: { code: 'NOT_ACCEPTABLE' } });
  }
});

Common Mistakes

  1. Inconsistent response structure across endpoints — Mixing {data: ...} in one endpoint and {result: ...} in another forces clients to handle multiple formats.
  2. Not setting Content-Type header — Express sets Content-Type automatically for res.json(), but ensure custom formatters set the correct type.
  3. Returning raw arrays without metadata — Returning just [user1, user2] without count or pagination info forces clients to guess the total.
  4. Exposing internal data in responses — Filter out sensitive fields like passwords, tokens, and internal IDs before sending responses.
  5. Missing error details for validation failures — Return field-level error details so clients can display specific error messages next to form fields.

Practice Questions

  1. What are the benefits of a consistent response envelope?
  2. How do you set custom response headers in Express?
  3. What should a paginated response include besides the items?
  4. How do you handle content negotiation for response formats?
  5. Challenge: Build a response formatting middleware that wraps all responses in a consistent envelope. It should support success, error, and paginated responses automatically based on the data returned from route handlers.

FAQ

Should I use a response envelope for all responses?

Yes, an envelope provides consistency. Include status, data, and error fields. Clients can parse any response the same way.

What fields should a standard success response include?

status: 'success', data: . Optionally add meta for pagination and timing information.

How do I filter sensitive fields from responses?

Use a toJSON() method on models or a utility that strips fields like password and ssn before sending the response.

Should I use camelCase or snake_case in JSON responses?

Either is fine. Be consistent. camelCase is common in JavaScript APIs. Use middleware or a serialization library to transform keys.

How do I format dates in JSON responses?

Use ISO 8601 format. Express serializes Date objects to ISO strings automatically in res.json().

Mini Project

Create a response formatting module with helper functions for success, error, created, paginated, and no-content responses. Integrate it into an Express app and ensure every endpoint uses the formatters. Add a middleware that catches unformatted responses and wraps them.

What's Next

Now learn about error middleware in Building REST APIs with Node.js.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro