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:
MemberDescription
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) => voidInvoked before requests are sent to the web API endpoint.
onResponse(response: AxiosResponse) => voidInvoked 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, and onError 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 instance
const 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.