For your Angular application, setting up a robust API layer with JWT authentication involves a few key steps. Here’s an approach to structure the Angular services to cleanly separate concerns and ensure modularity:
### Approach:
1. **Interceptor Setup:** Implement an HTTP interceptor in Angular to automatically inject the JWT token into all outgoing requests.
2. **Service Layer:** Develop a service for each entity (auth, courses, modules, levels) to handle API calls.
3. **Handling Token Expiry:** Ensure the interceptor can handle token expiration by fetching a new token if necessary.
### Step 1: Setup JWT Interceptor
The interceptor will attach the JWT token stored in localStorage or a similar client-side storage to each HTTP request.
**File: /src/app/core/interceptors/auth.interceptor.ts**
```typescript
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, switchMap } from 'rxjs/operators';
import { AuthService } from '../services/auth.service'; // Ensure AuthService has the logic to refresh tokens
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private authService: AuthService) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const token = localStorage.getItem('token'); // Get the token from storage
if (token) {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
}
return next.handle(request).pipe(
catchError(error => {
// Check if we received a 401 Unauthorized response
if (error.status === 401) {
return this.handle401Error(request, next);
}
return throwError(error);
})
);
}
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
return this.authService.refreshToken().pipe(
switchMap((token: any) => {
localStorage.setItem('token', token);
request = request.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
return next.handle(request);
}),
catchError((err) => {
this.authService.logout(); // Clear the token and logout if refresh fails
return throwError(err);
})
);
}
}
```
### Step 2: Setup Angular Services
Create separate service files for different API functionalities.
**File: /src/app/services/auth.service.ts**
```typescript
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class AuthService {
private apiUrl = 'http://localhost:3000/api/auth';
constructor(private http: HttpClient) {}
login(credentials: { email: string, password: string }): Observable<any> {
return this.http.post(`${this.apiUrl}/login`, credentials);
}
refreshToken(): Observable<any> {
// Implement refresh token logic here, possibly calling an API endpoint
return this.http.get(`${this.apiUrl}/refresh-token`);
}
logout() {
localStorage.removeItem('token');
// Handle logout logic
}
}
```
**Repeat similar setups for other services like Courses, Modules, Levels, etc., ensuring each service is modular and handles only its relevant API calls.**
### Step 3: Register Interceptor
Ensure your interceptor is registered in your Angular app module.
**File: /src/app/app.module.ts**
```typescript
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthInterceptor } from './core/interceptors/auth.interceptor';
import { NgModule } from '@angular/core';
// Other imports
@NgModule({
declarations: [
// Your components here
],
imports: [
// other modules
HttpClientModule
],
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
],
bootstrap: [AppComponent]
})
export class AppModule { }
```
### Conclusion:
This setup ensures that your Angular frontend can handle JWT tokens efficiently, with each service being responsible for specific parts of the API. This architecture helps keep your application organized and maintainable. Let me know if this approach works for you or if you need further customization!