Angular Resource API Service
This challenge focuses on building a robust and reusable resource API service in Angular. You will create a service that abstracts common HTTP operations for interacting with a backend API, allowing your application components to manage data without direct HTTP calls, promoting cleaner code and better separation of concerns.
Problem Description
Your task is to develop an Angular service that acts as a facade for interacting with a RESTful API. This service should encapsulate common CRUD (Create, Read, Update, Delete) operations for a generic "resource." The service should be designed to be easily extended to handle different types of resources.
Key Requirements:
- Generic Resource Handling: The service should be designed to work with any type of data (resource). This suggests using generics in TypeScript.
- CRUD Operations: Implement methods for:
get(id: string | number): Fetch a single resource by its ID.getAll(): Fetch all resources.create(resource: T): Create a new resource.update(resource: T): Update an existing resource.delete(id: string | number): Delete a resource by its ID.
- HTTP Client Integration: Utilize Angular's
HttpClientto make actual HTTP requests. - Base URL Configuration: The service should accept a base API URL during its creation or configuration.
- Error Handling: Implement basic error handling for HTTP requests, potentially returning an observable that emits an error.
- Observable Return Types: All methods that perform asynchronous operations should return RxJS Observables.
Expected Behavior:
- When
get(id)is called, it should make aGETrequest to[baseUrl]/[resourcePluralName]/{id}. - When
getAll()is called, it should make aGETrequest to[baseUrl]/[resourcePluralName]. - When
create(resource)is called, it should make aPOSTrequest to[baseUrl]/[resourcePluralName]with theresourceobject in the request body. - When
update(resource)is called, it should make aPUTorPATCHrequest to[baseUrl]/[resourcePluralName]/{resource.id}with theresourceobject in the request body. - When
delete(id)is called, it should make aDELETErequest to[baseUrl]/[resourcePluralName]/{id}. - The service should be injectable into other Angular components or services.
Edge Cases:
- What happens if the API returns an error (e.g., 404 Not Found, 500 Internal Server Error)? The observable should signal this error.
- Consider how to handle resource IDs (string vs. number). The methods should be flexible.
- Ensure the service can be configured with different base URLs and potentially different resource names.
Examples
Let's assume a hypothetical API structure:
- Base URL:
http://localhost:3000/api - Resource:
User - API Endpoints:
GET /api/usersGET /api/users/123POST /api/usersPUT /api/users/123DELETE /api/users/123
Example 1: Fetching all users
Assume HttpClient is configured to make requests to http://localhost:3000/api.
// In a component or another service
userService.getAll().subscribe(users => {
console.log(users); // Expected: [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
});
Example 2: Creating a new user
// In a component or another service
const newUser = { name: 'Charlie' };
userService.create(newUser).subscribe(createdUser => {
console.log(createdUser); // Expected: { id: 3, name: 'Charlie' }
});
Example 3: Handling an error (e.g., resource not found)
Assume http.get('/api/users/999') returns an error response with status 404.
// In a component or another service
userService.get(999).subscribe(
user => console.log(user),
error => {
console.error('Error fetching user:', error); // Expected: Logs an error message, possibly with status 404
}
);
Constraints
- The solution must be written in TypeScript.
- You must use Angular's
HttpClientmodule for all HTTP requests. - The service should be implemented as an Angular injectable service.
- The generic type
Trepresenting the resource should be properly utilized. - Consider how the resource name (e.g., "users") is determined. A common approach is to derive it from the generic type or pass it as a constructor argument.
- Assume the API returns JSON data.
Notes
- You'll likely need to create an interface to define the structure of your generic resource.
- Consider how to construct the correct API endpoint URLs.
- Think about using
mapandcatchErroroperators from RxJS for transforming responses and handling errors gracefully. - The challenge is to create a base service. You can then extend this base service for specific resources (e.g.,
UserService,ProductService) or use the base service directly if your API structure is very consistent. - For the
updatemethod, you might need to decide whether to usePUTorPATCH. A common approach is to default toPUTor allow it to be configured. - Think about how to handle the pluralization of resource names (e.g.,
Userbecomesusers). You might need a helper or a convention.