RealWorld API Validation Error Response Format

The RealWorld API returns validation errors as HTTP 422 (Unprocessable Entity) responses with a JSON body containing an errors object that maps field names to arrays of human-readable error messages.

The RealWorld specification serves as the standard for building full-stack demo applications, and the gothinkster/realworld repository provides canonical backend implementations. Understanding the RealWorld API validation error response format is essential for frontend developers, as it ensures consistent error handling across all endpoints that process user input, from authentication to content creation.

Structure of the Validation Error Response

HTTP Status Code 422

When input validation fails, route handlers throw an HttpException with status code 422 (Unprocessable Entity). This status indicates that while the server understands the request syntax, it cannot process the contained instructions due to semantic errors in the submitted data.

The Errors Object Schema

The response body follows a strict JSON schema enforced across all validated endpoints:

  • A top-level errors property containing an object
  • Keys represent invalid field names (e.g., email, username, title, body)
  • Values are arrays of strings describing specific validation failures
{
  "errors": {
    "email": ["can't be blank"],
    "password": ["can't be blank"],
    "username": ["has already been taken"]
  }
}

Clients receive this payload with a 422 status code and can iterate over the errors object to display messages next to relevant form fields.

Implementation in the RealWorld Codebase

The validation logic is implemented manually within individual route handlers rather than through centralized middleware. The HttpException class defined in apps/api/server/models/http-exception.model.ts provides the mechanism for formatting these responses, storing the HTTP status code and the error payload.

User Login Validation

In apps/api/server/routes/api/users/login.post.ts, missing credentials trigger specific error messages by manually constructing the payload:

if (!email) {
  throw new HttpException(422, { errors: { email: ["can't be blank"] } });
}
if (!password) {
  throw new HttpException(422, { errors: { password: ["can't be blank"] } });
}

Registration and Uniqueness Constraints

The registration endpoint in apps/api/server/routes/api/users/index.post.ts demonstrates both required field validation and database uniqueness checks:

if (!email) {
  throw new HttpException(422, { errors: { email: ["can't be blank"] } });
}
if (!username) {
  throw new HttpException(422, { errors: { username: ["can't be blank"] } });
}
if (!password) {
  throw new HttpException(422, { errors: { password: ["can't be blank"] } });
}

// Duplicate-check error
if (existingUserByEmail || existingUserByUsername) {
  throw new HttpException(422, {
    errors: {
      ...(existingUserByEmail ? { email: ["has already been taken"] } : {}),
      ...(existingUserByUsername ? { username: ["has already been taken"] } : {}),
    },
  });
}

Article Creation Validation

Content creation endpoints follow the same pattern. In apps/api/server/routes/api/articles/index.post.ts, required article fields generate 422 responses when omitted:

if (!title) {
  throw new HttpException(422, { errors: { title: ["can't be blank"] } });
}
if (!description) {
  throw new HttpException(422, { errors: { description: ["can't be blank"] } });
}
if (!body) {
  throw new HttpException(422, { errors: { body: ["can't be blank"] } });
}

Parsing Validation Errors in Client Applications

Frontend applications consuming the RealWorld API should expect the 422 status code and parse the errors object to display field-specific feedback. Since each key maps directly to a form input name, clients can iterate over the error entries and attach messages to their corresponding UI elements without additional mapping logic. This standardization allows a single error handler component to manage validation feedback for login forms, registration flows, and article editors alike.

Summary

  • The RealWorld API uses HTTP 422 status codes for all validation failures, signaling unprocessable entity errors.
  • Error responses contain a JSON object with a single errors property at the root level.
  • Each key in the errors object represents a field name, with values being arrays of descriptive error strings (e.g., ["can't be blank"] or ["has already been taken"]).
  • Implementation occurs in individual route handlers such as apps/api/server/routes/api/users/login.post.ts and apps/api/server/routes/api/articles/index.post.ts using the HttpException class from apps/api/server/models/http-exception.model.ts.
  • This standardized format enables client applications to handle validation feedback consistently across login, registration, article creation, and comment endpoints.

Frequently Asked Questions

What HTTP status code does the RealWorld API use for validation errors?

The API returns 422 Unprocessable Entity for all validation failures. This status indicates that while the request syntax is valid, the server cannot process the instructions due to semantic errors in the submitted data, such as missing required fields or duplicate values.

How does the RealWorld API handle multiple validation errors on different fields?

The API aggregates all validation failures into a single errors object within the response body. Each invalid field receives its own key, allowing clients to retrieve comprehensive feedback in one request. For example, submitting a blank registration form might return both email: ["can't be blank"] and password: ["can't be blank"] simultaneously.

Where is the RealWorld API error response format defined in the source code?

While the format is documented in the API specification, the implementation resides in apps/api/server/models/http-exception.model.ts where the HttpException class is defined. Individual routes instantiate this class with status code 422 and the specific errors payload, as seen in apps/api/server/routes/api/users/index.post.ts.

Does the RealWorld API use automatic validation middleware?

No, the reference implementation handles validation manually within each route handler. Routes check conditions individually—such as verifying field presence or database uniqueness—and construct the errors object explicitly before throwing HttpException, rather than using schema validation middleware or decorator-based validation systems.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →