Shared API Client
Relevant source files
The following files were used as context for generating this wiki page:
The Shared API Client (@dhc/api-client) is a typed fetch wrapper that provides a unified interface for the Web and Mobile applications to communicate with the Core API. It abstracts HTTP request logic, enforces type safety using Zod schemas defined in @dhc/contracts, and handles authentication via Bearer tokens.
Sources: packages/api-client/package.json:4-4, packages/api-client/src/client.ts:39-41
Core Architecture
The client utilizes a functional approach for initialization and request execution. It relies on ApiClientOptions for configuration, including the base URL and a token retrieval callback.
Client Initialization
You create the client by calling createApiClient. This function accepts a configuration object that includes:
baseUrl: The root address of the API (e.g.,https://api.example.com/v1).getAccessToken: A function that retrieves the current session token.fetch: An optional fetch implementation, defaulting to the environment's global fetch.
Sources: packages/api-client/src/client.ts:25-30, packages/api-client/src/client.ts:39-40
Request Lifecycle
Every API call flows through a central request method. This method builds the full URL with query parameters, sets standard headers, attaches the authorization token if present, and executes the fetch call.
The diagram shows the logical flow of a request from method invocation to typed result or error handling.
Sources: packages/api-client/src/client.ts:43-70, packages/api-client/src/client.test.ts:30-41
Authentication and Security
The client manages session security by automatically injecting tokens and supporting logout operations.
Authorization Headers
If the getAccessToken callback returns a value, the client appends the Authorization: Bearer <token> header to the request. This ensures that private endpoints, as defined in the OpenAPI contract, receive the necessary credentials.
Sources: packages/api-client/src/client.ts:51-52, packages/contracts/src/openapi.ts:742-743
Idempotency
For write operations that require retry safety, the client supports an idempotencyKey. The API uses this key to ensure the same request is not processed twice if retried.
Sources: packages/api-client/src/client.ts:34-34, packages/api-client/src/client.ts:50-50
Error Handling
The client translates standard HTTP error responses into a specialized ApiError class.
The ApiError Class
The ApiError provides structured access to error details returned by the backend, including a specific ErrorCode and field-level validation messages.
| Property | Type | Description |
|---|---|---|
status | number | The HTTP status code (e.g., 400, 401, 409, 500). |
code | ErrorCode | A machine-readable string identifying the error type (e.g., SLOT_TAKEN). |
message | string | A human-readable error description. |
requestId | string | The unique ID of the request, used for log correlation. |
fields | Record<string, string> | Validation error details mapped to specific input fields. |
Sources: packages/api-client/src/client.ts:11-23, packages/api-client/src/client.test.ts:47-58, apps/api/src/app.test.ts:63-71
Conversion Logic
If a response is not successful (response.ok is false), the client attempts to parse the body using the ErrorResponse schema. If parsing fails, it falls back to a generic INTERNAL or UNAUTHENTICATED error code.
Sources: packages/api-client/src/client.ts:117-130, packages/api-client/src/client.test.ts:60-65
API Methods
The client exposes high-level methods for common operations, reducing the need for manual request calls.
Common Operations
| Method | HTTP Method | Endpoint | Description |
|---|---|---|---|
getHealth | GET | /health | Checks the status and version of the API service. |
inspectInvite | POST | /auth/invites/inspect | Validates an invite token and returns account details. |
acceptInvite | POST | /auth/invites/accept | Submits a password to accept a staff invitation. |
completeInvite | POST | /auth/invites/complete | Submits an MFA code to finalize the invite process. |
logout | POST | /auth/logout | Invalidates the current session token. |
revokeStaffInvite | POST | /staff/invites/{id}/revoke | Cancels a pending invitation for a new staff member. |
Sources: packages/api-client/src/client.ts:75-115, packages/contracts/src/openapi.ts:118-185
Implementation Example
The following snippet demonstrates how the client is initialized and used within a React component to inspect an invitation.
// apps/web/app/invite/invite-flow.tsx
const api = useMemo(() => createApiClient({ baseUrl: apiBaseUrl }), [apiBaseUrl]);
api.inspectInvite(token.current)
.then((details) => setStep({ kind: 'password', details }))
.catch((e: unknown) => {
if (e instanceof ApiError && e.status === 404) setStep({ kind: 'invalid' });
else setError(messageFor(e));
});
Sources: apps/web/app/invite/invite-flow.tsx:37-56
Data Flow Integration
The API client serves as the bridge between the Zod-validated contracts and the application UI.
This sequence diagram illustrates how the client utilizes contracts to validate both successful responses and error shapes before returning data to the application.
Sources: packages/api-client/src/client.ts:68-69, packages/contracts/src/openapi.ts:725-738, packages/api-client/src/client.test.ts:14-28
The Shared API Client ensures consistent communication across the monorepo by centralizing request logic and strict type enforcement, facilitating easier maintenance and fewer runtime data mismatches.