|
1 | 1 | ---
|
2 |
| -title: Generic Data Endpoint |
3 |
| -description: Understand the powerful generic data endpoint and model registry pattern used in the API server. |
| 2 | +title: 'Pattern: Generic Data Endpoint & Registries' |
| 3 | +description: Understand the powerful generic data endpoint and registry patterns used in the API server. |
4 | 4 | ---
|
5 | 5 |
|
6 | 6 | import { Aside } from '@astrojs/starlight/components';
|
7 | 7 |
|
8 |
| -A significant architectural pattern in the API server is the use of a generic data endpoint powered by a `model_registry`. This design provides a single, consistent, and powerful interface for all standard CRUD (Create, Read, Update, Delete) operations, reducing code duplication and simplifying both the backend and client-side implementations. |
| 8 | +A significant architectural pattern in the API server is the use of a generic data endpoint powered by two central registries. This design provides a single, consistent, and powerful interface for all standard CRUD (Create, Read, Update, Delete) operations, reducing code duplication and simplifying both the backend and client-side implementations. |
9 | 9 |
|
10 | 10 | ### The Challenge: Avoiding Boilerplate
|
11 | 11 |
|
12 |
| -A typical REST API might have separate endpoints for each data type: |
13 |
| -- `/api/v1/headlines` |
14 |
| -- `/api/v1/topics` |
15 |
| -- `/api/v1/sources` |
16 |
| -- ...and so on. |
| 12 | +A typical REST API might have separate endpoints for each data type: `/headlines`, `/topics`, `/sources`, etc. This approach leads to a lot of boilerplate code, as the logic for handling GET, POST, PUT, and DELETE requests is often very similar for each model. |
17 | 13 |
|
18 |
| -While clear, this approach leads to a lot of boilerplate code, as the logic for handling GET, POST, PUT, and DELETE requests is often very similar for each model. |
| 14 | +### The Solution: A Single Endpoint with Registries |
19 | 15 |
|
20 |
| -### The Solution: A Single Endpoint with a Model Registry |
21 |
| - |
22 |
| -This API server solves the problem by using a single, dynamic endpoint: |
23 |
| - |
24 |
| -``` |
25 |
| -/api/v1/data |
26 |
| -``` |
27 |
| - |
28 |
| -This endpoint handles requests for **all** standard data models. The specific model to be acted upon is determined by a `?model=` query parameter. |
| 16 | +This API server solves the problem by using a single, dynamic endpoint: `/api/v1/data`. This endpoint handles requests for all standard data models, with the specific model determined by a `?model=` query parameter. |
29 | 17 |
|
30 | 18 | - `GET /api/v1/data?model=headline` -> Returns a list of headlines.
|
31 |
| -- `GET /api/v1/data/some-id?model=topic` -> Returns a single topic. |
32 | 19 | - `POST /api/v1/data?model=source` -> Creates a new source.
|
33 | 20 |
|
34 |
| -### The `model_registry` |
| 21 | +This is made possible by two key components: |
35 | 22 |
|
36 |
| -The magic behind this is the `model_registry`, a map defined in `lib/src/registry/model_registry.dart`. This registry links the string name of a model (e.g., "headline") to a `ModelConfig` object. |
| 23 | +1. **`ModelRegistry`**: This registry maps a model name (e.g., `"headline"`) to a `ModelConfig` object. The config defines the **rules** for the model, such as authorization requirements and type-specific functions (`fromJson`, `getOwnerId`). |
37 | 24 |
|
38 |
| -Each `ModelConfig` contains: |
39 |
| -- **Type-Specific Functions:** Functions like `fromJson` to deserialize the data correctly. |
40 |
| -- **Authorization Rules:** A detailed permission configuration for each HTTP method (GET, POST, PUT, DELETE), specifying what roles or permissions are required for that action. |
| 25 | +2. **`DataOperationRegistry`**: This registry maps a model name to the actual **functions** that perform the CRUD operations (e.g., the function to fetch all headlines, the function to create a new source). |
41 | 26 |
|
42 |
| -When a request comes in, the server's middleware looks up the `model` parameter in the registry, retrieves the correct configuration, and uses it to: |
43 |
| -1. Authorize the request based on the user's role and the action's permission requirements. |
44 |
| -2. Forward the request to the correct generic `DataRepository`. |
45 |
| -3. Deserialize and serialize data using the correct model class. |
| 27 | +When a request comes in, middleware uses the `ModelRegistry` to authorize the request and the route handlers use the `DataOperationRegistry` to execute the correct data operation. |
46 | 28 |
|
47 | 29 | <Aside type="note" title="Architectural Benefits">
|
48 |
| -- **DRY (Don't Repeat Yourself):** Eliminates redundant route handlers for CRUD operations. |
| 30 | +- **DRY (Don't Repeat Yourself):** Eliminates redundant route handlers. |
49 | 31 | - **Consistency:** Provides a uniform API for all data models.
|
50 |
| -- **Extensibility:** Adding CRUD support for a new data model is as simple as adding a new entry to the `model_registry`. |
51 |
| -- **Centralized Authorization:** Security rules for data access are defined in one central, easy-to-audit location. |
| 32 | +- **Extensibility:** Adding CRUD support for a new model is as simple as adding entries to the two registries. |
| 33 | +- **Centralized Logic:** Authorization rules and data operations are defined in central, easy-to-audit locations. |
| 34 | +</Aside> |
| 35 | + |
| 36 | +<Aside type="tip"> |
| 37 | +For a complete, step-by-step walkthrough of how the endpoint, registries, and middleware work together, see the [**Architectural Deep Dive: Data Access Flow**](./data-access-flow) guide. |
52 | 38 | </Aside>
|
0 commit comments