Services
Services are focused classes designed to interact with web API endpoints. As a good design pattern, a service should:
- Only interact with a single domain.
- Only provide features from the domain which are relevant to the theme of the service. For example, a user service should be focused on methods supporting user-related functionalities; adding order-related data would make for poor encapsulation.
Technical
- While each generated service resides in its own file and class, all services extend a predefined BaseService class to provide centralized functionality.
- Each service can specify a unique web API endpoint with which to interact - or none at all for services providing local functionality (e.g., wrapper storage mechanism over LocalStorage).
- Each service has access to the following protected members:
Member | Description |
---|---|
api (field) type: Axios | Helper to invoke web APIs. Individual service methods need only specify their endpoint paths relative to the registered root domain. |
onRequest(request: AxiosRequestConfig) => void | Invoked before requests are sent to the web API endpoint. |
onResponse(response: AxiosResponse) => void | Invoked before responses are handled by a Service’s methods. |
onError(data: any) | Invoked for errors during request or response. |
The Default Service Class
The Service
class is a base class that provides a foundation for all services in the application. It includes the following features:
- An
api
field that is an Axios instance configured with the base URL and default headers. - A constructor that sets up request and response interceptors to log requests and responses.
onRequest
,onResponse
, andonError
methods that can be overridden in derived classes to handle request, response, and error events.
import axios, { AxiosError, AxiosResponse } from "axios";import envConfig from "@/config/env";export class Service { protected api = axios.create({ baseURL: envConfig.apiBaseUrl, headers: { "Content-Type": "application/json", }, }); constructor() { // Set up request/response interceptors this.api.interceptors.request.use((request) => { this.onRequest(request); return request; }); this.api.interceptors.response.use( (response) => { this.onResponse(response); return response; }, (error: AxiosError) => { this.onError(error); return Promise.reject(error); } ); } protected onRequest(request: any): void { // Override in derived classes } protected onResponse(response: AxiosResponse): void { // Override in derived classes } protected onError(error: AxiosError): void { // Override in derived classes }}export default Service;
Example: Creating a Service Using the CLI Tool
To create a service using the CLI tool, use the following command:
frontier vue add service
This will generate a new service file extending the BaseService class.
Example Service Template
Here is an example of a default service that references a public API to perform basic CRUD operations:
import { AxiosRequestConfig, AxiosResponse } from "axios";import { Service } from "./base";class ExampleService extends Service { // [Public] Methods // Fetch all items public async getItems(): Promise<any> { return this.api .get("/items") .then((response: AxiosResponse<any>) => { return response.data; }) .catch(this.onError); } // Fetch a single item by ID public async getItem(id: number): Promise<any> { return this.api .get(`/items/${id}`) .then((response: AxiosResponse<any>) => { return response.data; }) .catch(this.onError); } // Create a new item public async createItem(item: any): Promise<any> { return this.api .post("/items", item) .then((response: AxiosResponse<any>) => { return response.data; }) .catch(this.onError); } // Update an existing item by ID public async updateItem(id: number, item: any): Promise<any> { return this.api .put(`/items/${id}`, item) .then((response: AxiosResponse<any>) => { return response.data; }) .catch(this.onError); } // Delete an item by ID public async deleteItem(id: number): Promise<any> { return this.api .delete(`/items/${id}`) .then((response: AxiosResponse<any>) => { return response.data; }) .catch(this.onError); } // [Protected] Event Handlers protected onRequest(request: AxiosRequestConfig): void { console.log("Request:", request); } protected onResponse(response: AxiosResponse): void { console.log("Response:", response); } protected onError(error: any): void { console.error("Error:", error); }}// Export the service instanceconst service = new ExampleService();export { service as default, service as ExampleService };
In this example, ExampleService
is a service class that extends BaseService
and interacts with a public API to perform basic CRUD operations. The service includes methods for getting items, getting a single item by ID, creating an item, updating an item, and deleting an item. Each method uses the api
field to make HTTP requests and handles errors through the onError
method. The onRequest
and onResponse
methods provide hooks for logging requests and responses.