Hone logo
Hone
Problems

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 HttpClient to 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 a GET request to [baseUrl]/[resourcePluralName]/{id}.
  • When getAll() is called, it should make a GET request to [baseUrl]/[resourcePluralName].
  • When create(resource) is called, it should make a POST request to [baseUrl]/[resourcePluralName] with the resource object in the request body.
  • When update(resource) is called, it should make a PUT or PATCH request to [baseUrl]/[resourcePluralName]/{resource.id} with the resource object in the request body.
  • When delete(id) is called, it should make a DELETE request 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/users
    • GET /api/users/123
    • POST /api/users
    • PUT /api/users/123
    • DELETE /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 HttpClient module for all HTTP requests.
  • The service should be implemented as an Angular injectable service.
  • The generic type T representing 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 map and catchError operators 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 update method, you might need to decide whether to use PUT or PATCH. A common approach is to default to PUT or allow it to be configured.
  • Think about how to handle the pluralization of resource names (e.g., User becomes users). You might need a helper or a convention.
Loading editor...
typescript