# How Mini-Shop Server Implements Centralized Error Handling with APIException

> Discover how Mini Shop Server centralizes API error handling with APIException. Standardize error responses with HTTP status, error codes, and messages for cleaner development.

- Repository: [A粒麦子/mini-shop-server](https://github.com/allen7d/mini-shop-server)
- Tags: how-to-guide
- Published: 2026-02-24

---

**The Mini-Shop Server implements centralized error handling through a hierarchical `APIException` class that standardizes every error response into a JSON payload containing an HTTP status code, application-specific error code, and human-readable message.**

The allen7d/mini-shop-server repository demonstrates a robust pattern for API error handling in Flask applications. By leveraging a single base exception class and a global error handler, the project ensures consistent JSON error responses across all endpoints while maintaining clean separation between business logic and error formatting.

## The APIException Base Class in app/core/error.py

The foundation of the error handling system resides in [`app/core/error.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/core/error.py), where the `APIException` class inherits from Flask's `werkzeug.exceptions.HTTPException`. This design choice allows the exception to function as both a Python exception and a valid WSGI response.

The class stores three critical attributes:

- `code` — The HTTP status code (defaults to 500)
- `error_code` — An application-specific numeric identifier (defaults to 999)  
- `msg` — A human-readable error description

By overriding `get_body()`, the class returns a JSON payload containing `msg`, `error_code`, and the request URL. The `get_headers()` method sets the appropriate `Content-Type` header. Additionally, implementing `__call__` enables the exception to act as a WSGI callable, allowing Flask to return it directly as an HTTP response.

## Global Error Handler Registration

In [`app/__init__.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/__init__.py), the `handle_error()` function registers a catch-all exception handler that intercepts every error raised within the application. This handler implements intelligent classification logic:

- **Existing APIException instances** are returned unchanged, preserving their specific configuration
- **Standard HTTPException objects** (like 404 or 403 errors) are wrapped in a new `APIException`, preserving the original HTTP code while translating the description into the `msg` field  
- **Database integrity errors** (such as duplicate key violations) trigger conversion to `RepeatException`
- **Unhandled exceptions** become generic `ServerError` instances, except in debug mode where the original traceback is preserved for debugging

This centralized approach eliminates the need for try-catch blocks in every view function while ensuring no exception escapes the application unformatted.

## Domain-Specific Error Subclasses

Concrete error types live in [`app/libs/error_code.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/libs/error_code.py), defining dozens of subclasses like `UserNotFound`, `ProductNotFound`, and `TokenException`. Each subclass configures its own `code`, `error_code`, and default `msg` values.

Because all domain errors inherit from `APIException`, they automatically gain the JSON serialization behavior and WSGI compatibility. Developers can raise these exceptions anywhere in the service layer without importing Flask-specific response utilities.

## Self-Documenting Error Registry

The project includes a unique developer experience feature in [`app/extensions/default_view/__init__.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/extensions/default_view/__init__.py). At application startup, this extension introspects the `app.libs.error_code` module, identifies every `APIException` subclass, and sorts them by `error_code`. It then renders an HTML table at the `/error_code` endpoint, providing a comprehensive reference of all possible API errors without requiring source code inspection.

## Practical Implementation Examples

Raising a domain-specific error requires no additional context or response formatting:

```python

# app/api/v1/product.py

from app.libs.error_code import ProductNotFound

@bp.route('/<int:pid>', methods=['GET'])
def get_product(pid):
    product = Product.query.get(pid)
    if not product:
        raise ProductNotFound()
    return jsonify(product.to_dict())

```

When `ProductNotFound()` is raised, the global handler catches it and returns a response like:

```json
{
  "msg": "未查询到数据",
  "error_code": 10100,
  "request_url": "GET /api/v1/product/123"
}

```

The HTTP status code matches the subclass configuration (404 in this case).

For database operations, unhandled `IntegrityError` exceptions bubble up to the global handler, which converts them to `RepeatException` with an appropriate message indicating duplicate data violations.

## Summary

- **Single inheritance hierarchy**: All errors extend `APIException` in [`app/core/error.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/core/error.py) to ensure consistent JSON formatting
- **Global interception**: The `handle_error` function in [`app/__init__.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/__init__.py) catches all exceptions and applies intelligent classification  
- **Domain specificity**: [`app/libs/error_code.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/libs/error_code.py) contains concrete error types like `ProductNotFound` with predefined codes and messages
- **Automatic documentation**: The `default_view` extension generates an HTML error code reference page by introspecting exception subclasses
- **Clean usage pattern**: Developers raise exceptions anywhere in the codebase without managing response formatting or HTTP headers

## Frequently Asked Questions

### What is the difference between code and error_code in APIException?

The `code` attribute represents the HTTP status code sent to the client (such as 404 or 500), while `error_code` is an application-specific numeric identifier that provides granular error classification beyond standard HTTP semantics. This dual-code system allows clients to distinguish between different business logic failures that might share the same HTTP status, such as differentiating between a missing user (error_code 1001) and a missing product (error_code 10100) while both return HTTP 404.

### How does the Mini-Shop Server handle unexpected database integrity errors?

When SQLAlchemy raises an `IntegrityError` (such as duplicate key violations or foreign key constraints), the global error handler in [`app/__init__.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/__init__.py) catches these exceptions and converts them to `RepeatException` instances. This specialized subclass of `APIException` returns HTTP 400 with error code 10001, providing a clear message about data duplication without exposing sensitive database schema details or raw SQL errors to API consumers.

### Can developers add custom error types to the API?

Yes. Developers can create new error classes in [`app/libs/error_code.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/libs/error_code.py) by inheriting from `APIException` and defining appropriate `code`, `error_code`, and `msg` class attributes. Once defined, these exceptions can be imported and raised anywhere in the application, and they will automatically appear in the `/error_code` documentation page because the default view extension dynamically introspects all `APIException` subclasses at startup.

### How do I view all available error codes in the running application?

Navigate to the `/error_code` endpoint in your browser. This route, implemented in [`app/extensions/default_view/__init__.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/extensions/default_view/__init__.py), renders an HTML table listing every `APIException` subclass sorted by `error_code`, displaying their HTTP status codes, numeric error codes, and default messages. This self-documenting feature updates automatically when new error types are added to the codebase.