White Cross Frontend Services Template Guide
Hey guys! Let's dive into how we structure our frontend services here at White Cross. We've got this awesome template that makes sure our service layer is super organized, type-safe, and resilient. Think of it as our blueprint for building consistent and reliable API interactions. We'll be using the existing services in /frontend/src/services/ as our go-to examples, so make sure you peek at those if you get stuck. This template is all about making our lives easier and our code cleaner!
The Grand Overview: What's Inside Our Services Layer?
Alright, so what exactly are we packing into our services layer? It's a carefully crafted modular architecture designed for robustness and maintainability. At its core, we have the **core infrastructure**. This includes essentials like our ApiClient, the BaseApiService that provides handy CRUD operations, and all our configurations. Moving outwards, we have the **domain-specific APIs**. These are like specialized units for different parts of our application, such as authentication (authApi), student data (studentsApi), or appointments (appointmentsApi). Don't forget the **utility services** too! These handle crucial tasks like error management, caching strategies, and security protocols. We're big on **type safety** here, folks, using Zod for validation throughout the entire process. Plus, we've baked in **monitoring and resilience** patterns, like retries and circuit breakers, to keep things running smoothly even when the unexpected happens. This comprehensive approach ensures that every service we build is consistent, reliable, and easy to work with.
Unpacking the Directory Structure: A Place for Everything
Let's break down the folder structure, because organization is key, right? It's designed to be intuitive and scalable. The main services/ directory is our central hub. Inside, you'll find:
index.ts: This is the main export file, acting as a gateway to all our services.config/: Here's where we keep our API configurations, likeapiConfig.ts, which handles Axios setup and interceptors.core/: This houses the foundational pieces. We've gotApiClient.tsfor our HTTP client,BaseApiService.tsfor abstracting CRUD,ResilientApiClient.tsfor retry logic,ServiceRegistry.tsfor service location,QueryHooksFactory.tsfor React Query integration, andApiMonitoring.tsfor performance tracking.modules/: This is where the magic happens for domain-specific logic. You'll find files likeauthApi.tsand then templates for other domains like{domain}Api.ts. For more complex domains, we can even create subfolders like{domain}/to keep things tidy, including types and validators.utils/: A collection of helpful utilities – thinkapiUtils.tsfor HTTP helpers,errorHandlers.tsfor consistent error management,validators.tsfor common Zod schemas, andtransformers.tsfor data manipulation.types/: Central place for re-exporting service-related types.security/: Contains crucial security modules likeSecureTokenManager.ts,CsrfProtection.ts, andPermissionChecker.ts.cache/: Handles our caching mechanisms with files likeApiCache.tsandCacheStrategies.ts.monitoring/: For keeping an eye on performance and errors, withApiMetrics.tsandErrorReporting.ts.resilience/: Where we define patterns like retries and circuit breakers inRetryStrategies.tsandCircuitBreaker.ts.
This structure might look a bit detailed, but trust me, it pays off massively in the long run by keeping everything predictable and easy to find.
File Templates: Your Building Blocks
Now, let's get into the nitty-gritty of the file templates. These are your starting points for creating new services. We've put a lot of thought into them to ensure consistency and cover all the bases.
1. Main Service Index (`index.ts`)
This file is super important because it's the single point of entry for all your services. Think of it as the master catalog. It exports everything from core infrastructure and utilities to specific domain APIs and security modules. This means when you need to use a service, you import it from a single, predictable location, making your code cleaner and easier to manage. For example, instead of importing Axios directly or digging through different utility folders, you'd grab apiInstance or handleApiError right from @/services. We also export common types here, so you don't have to hunt them down. When adding a new domain API, you'll update this file to include its exports, ensuring it's discoverable by the rest of the application. We've structured it logically, starting with core exports and moving towards more specific ones like authentication and domain-specific APIs. We've also included placeholders like {domain}Api and {Domain}Api, which you'll replace with your actual domain names (e.g., appointmentsApi, AppointmentsApi). It's all about creating a unified and well-organized service layer that promotes modularity and reusability across the White Cross application. The comments within the template also provide helpful context about the purpose and dependencies of each export, further aiding in understanding and maintenance. Remember to update the date placeholder too!
2. Domain API Service (`modules/{domain}Api.ts`)
This is where the real action happens for each specific domain. The {domain}Api.ts file is designed to encapsulate all the API operations for a particular feature, like managing users, products, or anything else. It’s built using our BaseApiService pattern, but with domain-specific logic, validation, and types. First off, you'll see the interface definition, like {Domain}Api, which outlines all the available methods – think basic CRUD (getAll, getById, create, update, delete) plus any advanced operations specific to that domain (like getStatistics, bulkUpdate, export, or import). We also define validation schemas using Zod for input data (like create{Domain}Schema and update{Domain}Schema) and filters ({domain}FiltersSchema). This is crucial for ensuring data integrity. The actual implementation class, {Domain}ApiImpl, extends BaseApiService (or uses the apiInstance directly) and performs the HTTP requests. It meticulously handles request building, response parsing, and error management using our utility functions like buildUrlParams and handleApiError. Crucially, we've integrated audit logging here, so every significant action (read, create, update, delete) is recorded for compliance and debugging. We use placeholders like {domain-kebab} for the API endpoint path (e.g., user-management) and constants like {DOMAIN} for audit resource types. This template provides a robust starting point, ensuring that each domain's API service is type-safe, validated, auditable, and follows consistent patterns. Remember to fill in the specific fields for your domain's schemas and types. The extensive comments within the template also serve as a detailed guide, explaining the purpose, dependencies, and critical path of the service. Don't forget to update the date and work item placeholders!
3. Base API Service (`core/BaseApiService.ts`)
The BaseApiService.ts file is the powerhouse behind our CRUD operations. It's an abstract class that provides a solid foundation for all our domain-specific API services. This means you don't have to reinvent the wheel every time you need to fetch, create, update, or delete data. It comes equipped with generic methods for these common operations, making your life so much easier. It takes an ApiClient instance, a baseEndpoint, and optional Zod schemas for validation right in its constructor. This allows us to enforce data integrity right at the service level. The `CrudOperations` interface clearly defines the contract that all services should adhere to, ensuring consistency. We’ve got methods like getAll, getById, create, update, and delete, all implemented with built-in error handling via the handleError method. This method is key because it transforms raw errors (like Zod validation errors or network issues) into a standardized ApiError format, making error management predictable across the application. We also include a handy buildUrl helper. By extending this base class, your domain services inherit all these functionalities, allowing you to focus on the unique aspects of your domain's API interactions. This pattern significantly reduces boilerplate code, promotes reusability, and ensures a high level of code quality and maintainability throughout the White Cross frontend. Just remember to update the date and work item placeholders when using this template!
4. API Configuration (`config/apiConfig.ts`)
This file, apiConfig.ts, is where we set up our global Axios instance and all the essential configurations that govern our API requests. It’s the central nervous system for all HTTP communication. We create a single apiInstance using axios.create, setting the base URL, timeout, and default headers like Content-Type and Accept. This instance is then used throughout our services. The real magic happens with the interceptors. The request interceptor is where we inject dynamic logic before a request is sent. This includes adding the authentication token (retrieved securely using secureTokenManager), attaching CSRF tokens for security, and adding a unique request ID for tracing purposes. We also start performance tracking here using performance.now(). The response interceptor is equally important. It handles recording performance metrics (like request duration) using our apiMetrics utility. More critically, it manages error handling, especially for authentication issues. If we get a 401 Unauthorized error, it attempts to refresh the access token using the refresh token. If the refresh is successful, it retries the original request with the new token; otherwise, it clears all tokens and redirects the user to the login page. This automatic refresh mechanism ensures a seamless user experience. We also include basic network error handling. Finally, this file sets up other security features like CSRF protection and exports utility functions (tokenUtils) for managing authentication state, making it a cornerstone of our secure and efficient API layer. Keep those date and work item placeholders updated!
5. API Utilities (`utils/apiUtils.ts`)
The apiUtils.ts file is packed with essential helper functions that streamline common API-related tasks. Think of it as our toolkit for dealing with HTTP requests and responses. We've got functions for robust error handling, transforming raw Axios errors into a consistent ApiError format, which makes managing issues across the app much simpler. For data extraction, extractApiData and extractApiDataOptional help us pull the actual data payload from various API response structures, ensuring we always get what we expect. Building query parameters for URLs is a breeze with buildUrlParams and buildPaginationParams, which intelligently construct search strings for API requests. We also include utilities for date formatting (formatDateForApi, parseDateFromApi) to ensure dates are handled correctly between the client and server. One of the most powerful functions here is withRetry, which implements a robust retry mechanism with exponential backoff for transient network failures or server errors, significantly improving resilience. For handling file uploads, createFormData makes it easy to package data into a FormData object. We've also included type guards like isApiResponse and isPaginatedResponse to help with runtime type checking. Lastly, this file houses our ApiCache class and the withCache higher-order function, enabling efficient response caching to reduce load times and server requests. This collection of utilities is fundamental to building reliable and performant API interactions in our frontend services. Make sure to update the date placeholder!
Implementation Guidelines: Best Practices for Services
To keep things running smoothly and ensure everyone's on the same page, we've laid out some clear guidelines for implementing our frontend services. Following these will make your life easier and the codebase much more consistent.
1. Service Naming Conventions
Consistency starts with naming! For API files, we use the {domain}Api.ts format, keeping it camelCase. Interfaces should be in PascalCase, like {Domain}Api. When defining API endpoints in your URLs, stick to kebab-case (e.g., /api/user-management). Method names should be descriptive and follow common action verbs: getAll, getById, create, update, and delete are our standards. Adhering to these conventions makes it instantly clear what a file or method does, improving readability and maintainability across the entire White Cross project.
2. Validation Strategy
Data integrity is non-negotiable, guys! We use Zod schemas for comprehensive validation. This means you need to create detailed schemas for all inputs to your API methods and also for validating the responses coming back from the server. This dual validation ensures that only valid data enters your system and that you're receiving data in the expected format. We emphasize runtime validation – checking data as it comes and goes. When validation fails, provide clear, user-friendly error messages. This ties directly into type safety; your Zod schemas should always align perfectly with your TypeScript interfaces, acting as a single source of truth for data structure.
3. Error Handling
We aim for predictable error management. All errors should be transformed into a consistent ApiError format, which includes a message, an optional code, the HTTP status, and any specific details. We handle different HTTP status codes appropriately – for example, 404s, 500s, and authentication errors all have specific treatment. Crucially, we implement retry logic for transient failures, like temporary server issues (5xx errors). This is typically handled using the withRetry utility. The goal is to provide meaningful error messages to the user, whether it's a validation error, a network issue, or a server problem, without exposing raw technical details.
4. Authentication & Security
Security is paramount. We rely on the SecureTokenManager for secure storage and retrieval of authentication tokens (both access and refresh tokens). The system includes logic for automatic, transparent token refresh to ensure uninterrupted user sessions. When necessary, we include CSRF tokens in requests to protect against cross-site request forgery attacks. Before performing sensitive operations, always ensure permission checks are in place, validating that the current user has the necessary rights to execute the action. This layered security approach protects our application and user data.
5. Monitoring & Logging
Keeping an eye on performance and potential issues is vital. We use ApiMetrics to track API call duration, success rates, and other performance indicators. All errors encountered during API interactions should be logged using our error tracking utilities, providing context for debugging. For compliance and auditing purposes, audit trails are generated for all significant operations (like creating or deleting records). Where possible, include unique request IDs in headers to aid in distributed tracing across different services.
6. Caching Strategy
To boost performance and reduce server load, we implement response caching where appropriate. This is particularly useful for data that is requested frequently but changes infrequently. Use the ApiCache and withCache utilities for this. Always consider cache invalidation – when data is mutated (created, updated, deleted), you need to clear the relevant cache entries to ensure users see the latest information. Set appropriate Time-To-Live (TTL) values for cached data to balance freshness and performance. Also, be mindful of memory usage; implement strategies to limit cache size and prevent potential memory leaks.
Service Integration Patterns: How to Use Them
Integrating these services into your frontend application is designed to be straightforward. We support common patterns used in modern web development, ensuring you can plug these services into your existing architecture with minimal fuss.
1. Redux Store Integration
If your application uses Redux for state management, integrating our services is seamless. You'll typically use Redux Toolkit's createAsyncThunk to handle asynchronous API calls. Within the thunk, you simply import the relevant service (e.g., {domain}Api from @/services) and call its methods, like getAll(filters). The data returned by the API call becomes the payload for your Redux action, which then updates your store. This pattern keeps your API logic contained within your Redux slices, making state management clean and predictable. The example shows how to fetch items using the {domain}Api.getAll method directly within an async thunk, passing any necessary filters. It's a clean and standard way to manage data fetching for your application's state.
2. React Component Usage
Using services directly within React components is also very common, especially for simpler data fetching needs or when using component-level state. You'll typically import the service you need (e.g., {domain}Api) and use it within a useEffect hook. Inside the effect, you can make the API call (like {domain}Api.getAll()), handle loading states, and update your component's state with the fetched data. Remember to include error handling using a try...catch block to gracefully manage potential API failures. This approach is great for scenarios where the data is closely tied to a specific component's lifecycle. The example demonstrates fetching items when the component mounts and managing loading indicators, providing a clear pattern for component-based data retrieval.
3. React Query Integration
For more advanced data fetching, caching, and state synchronization needs, integrating with React Query is highly recommended. You can create custom hooks that wrap your service calls using React Query's useQuery (or useMutation for write operations). Your custom hook will define the queryKey (essential for caching and invalidation) and the queryFn, which simply calls the appropriate service method (e.g., {domain}Api.getAll(filters)). You can configure options like staleTime directly in the hook. This pattern leverages React Query's powerful features like automatic refetching, background updates, and intelligent caching, significantly simplifying complex data management in your React components. The example shows a straightforward hook `use{Domain}Items` that utilizes React Query to fetch items, making it easy to consume in any component while benefiting from React Query's optimizations.
Replacement Placeholders: Your Customization Guide
When you're creating a new service using this template, you'll need to replace a few placeholders to tailor it to your specific domain. Pay close attention to these:
{domain}: This should be replaced with the lowercase name of your domain, likeappointmentsorinventory.{Domain}: This is the PascalCase version of your domain name, such asAppointmentsorInventory.{DOMAIN}: For audit resource types, use the UPPERCASE version, likeAPPOINTMENTSorINVENTORY.{domain-kebab}: Use the kebab-case version of your domain name for API endpoint paths, e.g.,user-managementorproduct-catalog.{date}: Replace this with the current date in YYYY-MM-DD format.WF-COMP-XXX: This placeholder should be replaced with the actual work item or ticket number associated with the service you're creating.
Integration Checklist: Ensuring Quality
Before you consider your new service complete, run through this checklist to make sure everything is up to snuff:
- [ ] Service follows the established naming conventions.
- [ ] Validation schemas are comprehensive and match backend constraints.
- [ ] Error handling follows the consistent
ApiErrorformat. - [ ] Authentication and security measures are implemented correctly.
- [ ] Audit logging is included for all relevant CRUD operations.
- [ ] Performance monitoring is integrated where applicable.
- [ ] Caching strategy is appropriate for the data access patterns.
- [ ] Service is properly exported in the main
services/index.tsfile. - [ ] TypeScript types are comprehensive, accurate, and exported correctly.
- [ ] Unit tests cover all the service methods effectively.
- [ ] Integration tests verify API contract compliance.
- [ ] Documentation includes clear usage examples.
This template is designed to be your best friend when building out the frontend services for White Cross. It ensures all our services are built with consistency, reliability, and maintainability in mind, making our entire application stronger. Happy coding, everyone!