Building a Vue 3 API Client with TypeScript
This challenge focuses on creating a robust and type-safe API client within a Vue 3 application using TypeScript. You'll learn how to define API endpoints, handle requests and responses, and manage loading and error states, leading to more maintainable and predictable frontend interactions with backend services.
Problem Description
Your task is to build a modular API client service for a Vue 3 application that interacts with a hypothetical REST API. This service should abstract away the complexities of making HTTP requests, handle different HTTP methods, manage request/response data transformation, and provide clear states for loading and errors.
Key Requirements:
- Create a dedicated API service file: This service should be a standalone TypeScript module, independent of any specific Vue components.
- Define API endpoints: Implement functions for common HTTP methods like
GET,POST,PUT, andDELETE. Each function should accept the endpoint path, optional request data, and potentially configuration options. - Type Safety: Utilize TypeScript generics to ensure that request payloads and response data are strongly typed. This will help prevent runtime errors and improve developer experience.
- Error Handling: Implement a mechanism to catch and report API errors. The service should expose an
errorstate or a way to access caught errors. - Loading State: Provide a way to track whether an API request is currently in progress. The service should expose a
loadingstate or a similar indicator. - Base URL Configuration: Allow for a configurable base URL for the API.
- JSON Handling: Assume the API returns and accepts JSON data.
Expected Behavior:
When a function in the API service is called:
- The
loadingstate should becometrue. - An HTTP request should be made to the specified endpoint with the correct method and payload.
- If the request is successful, the typed response data should be returned.
- If the request fails, an error should be caught and accessible, and the
loadingstate should becomefalse. - Regardless of success or failure, the
loadingstate should eventually becomefalse.
Edge Cases to Consider:
- Requests with no body (e.g.,
GET,DELETE). - Requests with varying payload structures.
- API responses that might not always conform to expected types (though for this challenge, we'll assume consistent API behavior for simplicity, but good practice should be considered).
- Network interruptions.
Examples
Example 1: Fetching a list of users
Let's assume a GET request to /users returns an array of user objects.
// Assume this interface is defined elsewhere
interface User {
id: number;
name: string;
email: string;
}
// Within your API service
// ...
async getUsers(): Promise<User[]> {
// This function will internally call your fetch/axios wrapper
// and return the typed array of users.
// loading will be true during the request and false afterwards.
// error will be null on success.
}
// ...
Example 2: Creating a new post
Let's assume a POST request to /posts accepts a PostData object and returns the created Post object.
// Assume these interfaces are defined elsewhere
interface PostData {
title: string;
body: string;
userId: number;
}
interface Post extends PostData {
id: number;
createdAt: string;
}
// Within your API service
// ...
async createPost(postData: PostData): Promise<Post> {
// This function will internally call your fetch/axios wrapper
// with the POST method and postData.
// It will return the typed Post object.
}
// ...
Example 3: Deleting a resource
A DELETE request to /items/:id.
// Within your API service
// ...
async deleteItem(itemId: number): Promise<void> {
// This function will make a DELETE request to `/items/${itemId}`.
// It might return void or a success/failure indicator.
}
// ...
Constraints
- HTTP Client: You can use either the native
fetchAPI or a library likeaxios. For this challenge, choose one and stick with it. - TypeScript Version: Vue 3 projects typically use TypeScript 4.0 or higher.
- API Base URL: The base URL should be configurable, defaulting to a placeholder like
https://api.example.com/v1. - Error Structure: For simplicity, assume API errors will be returned as JSON with an
errorproperty containing a message string. If the response is not JSON, assume a generic network error. - Data Serialization/Deserialization: Assume automatic JSON serialization for request bodies and deserialization for responses.
Notes
- Consider organizing your API service into separate files or modules for different resource categories (e.g.,
userApi.ts,postApi.ts) as your application grows. - Think about how you'll integrate this API service with Vue components. A common approach is to use Pinia or Vuex stores, or simply inject the service into components.
- For advanced scenarios, you might consider adding request interceptors for things like adding authentication tokens or logging.
- While we're focusing on basic CRUD operations, the principles can be extended to handle more complex API interactions.
- The goal is to create a reusable and type-safe layer that shields your Vue components from the direct details of network communication.