Skip to content

Commit 3d881ce

Browse files
committed
docs(ht_api): refactor and document data endpoint architecture
- Remove unused commented-out code in countries_client_provider.dart - Add detailed comments explaining the individual provider pattern - Document the reasoning behind the centralized provider approach for core data models - Enhance documentation in model_registry.dart to clarify its role in the generic data endpoint - Expand comments in data endpoint middleware to explain its crucial functions
1 parent 5ae636f commit 3d881ce

File tree

3 files changed

+96
-53
lines changed

3 files changed

+96
-53
lines changed
Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,37 @@
1-
// import 'package:dart_frog/dart_frog.dart';
2-
// import 'package:ht_countries_client/ht_countries_client.dart';
3-
// import 'package:ht_countries_inmemory/ht_countries_inmemory.dart';
4-
5-
// /// Provides an instance of [HtCountriesClient] to the request context.
6-
// ///
7-
// /// This middleware uses the inmemory implementation
8-
// /// [HtCountriesInMemoryClient].
9-
// Middleware countriesClientProvider() {
10-
// // Create the client instance once when the middleware is initialized.
11-
// // This assumes the in-memory client is cheap to create and can be reused
12-
// // across requests.
13-
// final HtCountriesClient client = HtCountriesInMemoryClient();
14-
15-
// return (Handler innerHandler) {
16-
// return (RequestContext context) {
17-
// // Provide the existing client instance to this request's context.
18-
// final updatedContext = context.provide<HtCountriesClient>(()=> client);
19-
// // Call the next handler in the chain with the updated context.
20-
// return innerHandler(updatedContext);
21-
// };
22-
// };
23-
// }
1+
// Dart Frog Dependency Injection Pattern: Individual Providers
2+
//
3+
// This directory (`lib/src/providers`) and files like this one demonstrate
4+
// a common pattern in Dart Frog for providing dependencies using dedicated
5+
// middleware for each specific dependency (e.g., a client or repository).
6+
//
7+
// Example (Conceptual - Code Removed):
8+
// ```dart
9+
// // Middleware countriesClientProvider() {
10+
// // final HtCountriesClient client = HtCountriesInMemoryClient();
11+
// // return provider<HtCountriesClient>((_) => client);
12+
// // }
13+
// ```
14+
// This middleware would then be `.use()`d in a relevant `_middleware.dart` file.
15+
//
16+
// --- Why This Pattern Isn't Used for Core Data Models in THIS Project ---
17+
//
18+
// While the individual provider pattern is valid, this specific project uses a
19+
// slightly different approach for its main data models (Headline, Category, etc.)
20+
// to support the generic `/api/v1/data` endpoint.
21+
//
22+
// Instead of individual provider middleware files here:
23+
// 1. Instances of the core data repositories (`HtDataRepository<Headline>`,
24+
// `HtDataRepository<Category>`, etc.) are created and provided directly
25+
// within the top-level `routes/_middleware.dart` file.
26+
// 2. A `modelRegistry` (`lib/src/registry/model_registry.dart`) is used in
27+
// conjunction with middleware at `routes/api/v1/data/_middleware.dart` to
28+
// dynamically determine which model and repository to use based on the
29+
// `?model=` query parameter in requests to `/api/v1/data`.
30+
//
31+
// This centralized approach in `routes/_middleware.dart` and the use of the
32+
// registry were chosen to facilitate the generic nature of the `/api/v1/data`
33+
// endpoint.
34+
//
35+
// This `providers` directory is kept primarily as a reference to the standard
36+
// individual provider pattern or for potential future use with dependencies
37+
// that don't fit the generic data model structure.

lib/src/registry/model_registry.dart

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,13 @@ import 'package:ht_shared/ht_shared.dart';
88
/// {@template model_config}
99
/// Configuration holder for a specific data model type [T].
1010
///
11-
/// Contains the necessary functions (deserialization, ID extraction) required
12-
/// to handle requests for this model type within the generic `/data` endpoint.
11+
/// This class encapsulates the type-specific operations (like deserialization
12+
/// from JSON and ID extraction) needed by the generic `/api/v1/data` endpoint
13+
/// handlers. It allows those handlers to work with different data models
14+
/// without needing explicit type checks for these common operations.
15+
///
16+
/// An instance of this config is looked up via the [modelRegistry] based on the
17+
/// `?model=` query parameter provided in the request.
1318
/// {@endtemplate}
1419
class ModelConfig<T> {
1520
/// {@macro model_config}
@@ -32,11 +37,19 @@ class ModelConfig<T> {
3237
// They will be created and provided directly in the main dependency setup.
3338

3439
/// {@template model_registry}
35-
/// Central registry mapping model name strings (used in API query params)
40+
/// Central registry mapping model name strings (used in the `?model=` query parameter)
3641
/// to their corresponding [ModelConfig] instances.
3742
///
38-
/// This registry is used by the middleware to look up the correct configuration
39-
/// and repository based on the `?model=` query parameter.
43+
/// This registry is the core component enabling the generic `/api/v1/data` endpoint.
44+
/// The middleware (`routes/api/v1/data/_middleware.dart`) uses this map to:
45+
/// 1. Validate the `model` query parameter provided by the client.
46+
/// 2. Retrieve the correct [ModelConfig] containing type-specific functions
47+
/// (like `fromJson`) needed by the generic route handlers (`index.dart`, `[id].dart`).
48+
///
49+
/// While individual repositories (`HtDataRepository<Headline>`, etc.) are provided
50+
/// directly in the main `routes/_middleware.dart`, this registry provides the
51+
/// *metadata* needed to work with those repositories generically based on the
52+
/// request's `model` parameter.
4053
/// {@endtemplate}
4154
final modelRegistry = <String, ModelConfig>{
4255
'headline': ModelConfig<Headline>(
@@ -66,8 +79,9 @@ typedef ModelRegistryMap = Map<String, ModelConfig>;
6679

6780
/// Dart Frog provider function factory for the entire [modelRegistry].
6881
///
69-
/// This makes the registry available for injection, primarily for the
70-
/// middleware responsible for resolving the model type.
82+
/// This makes the `modelRegistry` map available for injection into the
83+
/// request context via `context.read<ModelRegistryMap>()`. It's primarily
84+
/// used by the middleware in `routes/api/v1/data/_middleware.dart`.
7185
final modelRegistryProvider = provider<ModelRegistryMap>(
7286
(_) => modelRegistry,
7387
); // Use lowercase provider function for setup

routes/api/v1/data/_middleware.dart

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,56 @@
22
// ignore_for_file: lines_longer_than_80_chars
33

44
import 'package:dart_frog/dart_frog.dart';
5-
import 'package:ht_api/src/registry/model_registry.dart'; // Adjust import if needed
5+
import 'package:ht_api/src/registry/model_registry.dart';
66

7-
/// Middleware for the /api/v1/data route.
7+
/// Middleware specific to the generic `/api/v1/data` route path.
88
///
9-
/// Responsibilities:
10-
/// 1. Reads the 'model' query parameter from the request.
11-
/// 2. Validates the 'model' parameter (must exist and be a key in the modelRegistry).
12-
/// 3. Reads the globally provided [ModelRegistryMap].
13-
/// 4. Looks up the corresponding [ModelConfig] for the requested model.
14-
/// 5. Provides the specific [ModelConfig<dynamic>] for the model downstream.
15-
/// 6. Provides the validated model name string downstream.
9+
/// This middleware is crucial for the functioning of the generic data endpoint.
10+
/// Its primary responsibilities are:
11+
///
12+
/// 1. **Read and Validate `model` Parameter:** Extracts the `model` query
13+
/// parameter from the incoming request URL (e.g., `?model=headline`).
14+
/// It ensures this parameter exists and corresponds to a valid key
15+
/// within the globally provided [ModelRegistryMap]. If validation fails,
16+
/// it immediately returns a 400 Bad Request response, preventing the
17+
/// request from reaching the actual route handlers (`index.dart`, `[id].dart`).
18+
///
19+
/// 2. **Look Up Model Configuration:** Reads the globally provided
20+
/// [ModelRegistryMap] (injected by `routes/_middleware.dart`) and uses the
21+
/// validated `modelName` to find the corresponding [ModelConfig] instance.
22+
/// This config contains type-specific functions (like `fromJson`) needed
23+
/// by the downstream handlers.
24+
///
25+
/// 3. **Provide Context Downstream:** Injects two crucial pieces of information
26+
/// into the request context for the route handlers (`index.dart`, `[id].dart`)
27+
/// to use:
28+
/// - The specific `ModelConfig<dynamic>` for the requested model.
29+
/// - The validated `modelName` as a `String`.
30+
///
31+
/// This allows the route handlers under `/api/v1/data/` to operate generically,
32+
/// using the provided `modelName` to select the correct repository (which are
33+
/// also provided globally) and the `ModelConfig` for type-specific operations
34+
/// like deserializing request bodies.
1635
///
1736
/// If validation fails (missing/invalid model parameter), it returns a 400 Bad Request response immediately.
1837
Handler middleware(Handler handler) {
1938
return (context) async {
20-
// 1. Read the 'model' query parameter
39+
// --- 1. Read and Validate `model` Parameter ---
2140
final modelName = context.request.uri.queryParameters['model'];
22-
23-
// 2. Validate the 'model' parameter
2441
if (modelName == null || modelName.isEmpty) {
2542
return Response(
2643
statusCode: 400,
2744
body: 'Bad Request: Missing or empty "model" query parameter.',
2845
);
2946
}
3047

31-
// 3. Read the globally provided ModelRegistryMap
32-
// Assumes modelRegistryProvider is used in a higher-level middleware (e.g., routes/_middleware.dart)
48+
// --- 2. Look Up Model Configuration ---
49+
// Read the globally provided registry
3350
final registry = context.read<ModelRegistryMap>();
34-
35-
// 4. Look up the ModelConfig
51+
// Look up the config for the validated model name
3652
final modelConfig = registry[modelName];
3753

38-
// 2. (cont.) Validate model existence in registry
54+
// Further validation: Ensure model exists in the registry
3955
if (modelConfig == null) {
4056
return Response(
4157
statusCode: 400,
@@ -44,15 +60,14 @@ Handler middleware(Handler handler) {
4460
);
4561
}
4662

47-
// 5. & 6. Provide the ModelConfig and modelName downstream
48-
// We provide ModelConfig<dynamic> because the specific type T isn't known here.
49-
// The route handler will use this config along with the correct repository
50-
// instance (which should also be provided globally).
63+
// --- 3. Provide Context Downstream ---
64+
// Provide the specific ModelConfig and the validated modelName string
65+
// for the route handlers (`index.dart`, `[id].dart`) to use.
5166
final updatedContext = context
52-
.provide<ModelConfig<dynamic>>(() => modelConfig)
67+
.provide<ModelConfig<dynamic>>(() => modelConfig) // Provide the config
5368
.provide<String>(() => modelName); // Provide the validated model name
5469

55-
// Call the next handler in the chain
70+
// Call the next handler in the chain with the updated context
5671
return handler(updatedContext);
5772
};
5873
}

0 commit comments

Comments
 (0)