API Proxy Design
- 1. Overview
- 2. Proxy Architecture
- 3. Gateway Routes Configuration
- 4. Proxied Endpoints
- 5. Local Gateway Endpoints
- 6. Service Configuration
- 7. Request Flow
- 8. Generated API Clients
- 9. Error Handling
- 10. Security Integration
- 11. Performance Considerations
- 12. Monitoring and Observability
- 13. Related Documentation
1. Overview
The Registration Portal operates as a JHipster gateway application, proxying most API requests to the admin-service backend. This design provides a clean separation between the presentation layer (Angular) and the business logic layer (Spring Boot services).
Key Benefits:
-
Single Entry Point - All API calls go through gateway
-
Security Layer - OAuth2 token validation at gateway
-
Service Discovery - Gateway routes requests to correct service
-
Load Balancing - Can distribute requests across service instances
-
Circuit Breaking - Fault tolerance for backend failures
3. Gateway Routes Configuration
3.1. Route Definitions
spring:
cloud:
gateway:
routes:
- id: admin-service
uri: http://localhost:8080
predicates:
- Path=/api/**,/services/**
filters:
- RewritePath=/(?<segment>.*), /$\{segment}
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY,GATEWAY_TIMEOUT
methods: GET,POST,PUT,DELETE
backoff:
firstBackoff: 10ms
maxBackoff: 50ms
factor: 2
basedOnPreviousValue: false
- id: admin-service-websocket
uri: ws://localhost:8080
predicates:
- Path=/websocket/**
4. Proxied Endpoints
4.1. Person API
| Method | Frontend Path | Proxied To | Service |
|---|---|---|---|
GET |
|
admin-service |
PersonService |
POST |
|
admin-service |
PersonService |
PUT |
|
admin-service |
PersonService |
DELETE |
|
admin-service |
PersonService |
GET |
|
admin-service |
PersonService |
POST |
|
admin-service |
PersonService |
4.2. LinkedPerson API
| Method | Frontend Path | Proxied To | Service |
|---|---|---|---|
POST |
|
admin-service |
LinkedPersonService |
GET |
|
admin-service |
LinkedPersonService |
DELETE |
|
admin-service |
LinkedPersonService |
4.3. Form/Process API
| Method | Frontend Path | Proxied To | Service |
|---|---|---|---|
GET |
|
admin-service |
FormService |
POST |
|
admin-service |
FormService |
POST |
|
admin-service |
ProcessService |
POST |
|
admin-service |
ProcessService |
5. Local Gateway Endpoints
6. Service Configuration
6.1. ApplicationConfigService
The Angular application uses ApplicationConfigService to construct proxied endpoint URLs:
@Injectable({ providedIn: 'root' })
export class ApplicationConfigService {
private endpointPrefix = '';
setEndpointPrefix(endpointPrefix: string): void {
this.endpointPrefix = endpointPrefix;
}
getEndpointFor(api: string, microservice?: string): string {
if (microservice) {
return `${this.endpointPrefix}services/${microservice}/${api}`;
}
return `${this.endpointPrefix}${api}`;
}
}
Usage Examples:
// Proxied to admin-service
this.applicationConfigService.getEndpointFor('api/people', 'admin-service')
// Returns: "services/admin-service/api/people"
// Direct gateway endpoint
this.applicationConfigService.getEndpointFor('api/account')
// Returns: "api/account"
6.2. Service URL Construction
PeopleService Example:
@Injectable({ providedIn: 'root' })
export class PeopleService {
readonly peopleUrl = 'services/admin-service/api/people';
getPerson(id: number): Observable<IPerson> {
return this.personResourceService.getPerson(id)
.pipe(mergeMap(response => this.blobUtils.parseBlob<IPerson>(response)));
}
}
LinkedPersonStateService Example:
@Injectable({ providedIn: 'root' })
export class LinkedPersonStateService {
linkEdPersonUrl = this.applicationConfigService
.getEndpointFor('api/linked-people/link', 'admin-service');
linkPerson(id: number, type: string, userKey: string): Observable<ILinkedPerson> {
return this.http.post<ILinkedPerson>(
`${this.linkEdPersonUrl}/${id}/${type}?userKey=${userKey}&organisationId=8`,
{ observe: 'response' }
);
}
}
8. Generated API Clients
The portal uses generated TypeScript API clients from OpenAPI specifications:
8.1. Registration API TypeScript Client
import {
FormResourceService,
PersonResourceService,
MembershipPeriodResourceService
} from '@christhonie/registration-api-ts';
@Injectable({ providedIn: 'root' })
export class FormService {
private formResourceService = inject(FormResourceService);
getFormMembership(membershipPeriodId: number, userKey: string):
Observable<FormDTO> {
return this.formResourceService
.getFormMembership(membershipPeriodId, userKey)
.pipe(mergeMap(response => this.blobUtils.parseBlob<FormDTO>(response)));
}
}
Package: @christhonie/registration-api-ts
Key Classes:
-
FormResourceService -
PersonResourceService -
MembershipPeriodResourceService -
EventResourceService
9. Error Handling
9.1. Circuit Breaker
The gateway includes circuit breaker patterns for fault tolerance:
Configuration:
resilience4j:
circuitbreaker:
instances:
admin-service:
failureRateThreshold: 50
waitDurationInOpenState: 10s
slidingWindowSize: 10
minimumNumberOfCalls: 5
Behavior:
-
Closed - Normal operation, requests flow through
-
Open - Too many failures, requests immediately fail with 503
-
Half-Open - Testing if service recovered, limited requests allowed
9.2. Retry Logic
filters:
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY,GATEWAY_TIMEOUT
methods: GET,POST,PUT,DELETE
backoff:
firstBackoff: 10ms
maxBackoff: 50ms
factor: 2
Retry Scenarios:
-
502 Bad Gateway - Backend unreachable
-
504 Gateway Timeout - Backend slow to respond
-
Exponential backoff between retries
9.3. Timeout Configuration
HTTP Interceptor:
export class TimeoutInterceptor {
static getHeadersWithTimeout(timeout: number): HttpHeaders {
return new HttpHeaders({
'X-Timeout': timeout.toString()
});
}
}
// Usage in FormService
const headers = TimeoutInterceptor.getHeadersWithTimeout(15000);
return this.formResourceService.next(data, 'body', false, { headers });
Default Timeout: 2 minutes Form Submission Timeout: 15 seconds (extended for processing)
10. Security Integration
10.2. Request Headers
Outbound Headers (Angular → Gateway):
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
Accept-Language: en
Content-Type: application/json
Gateway → Admin Service:
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
X-Forwarded-For: 192.168.1.100
X-Forwarded-Proto: https
X-Forwarded-Host: app.example.com
11. Performance Considerations
11.1. Connection Pooling
spring:
cloud:
gateway:
httpclient:
pool:
max-connections: 100
max-idle-time: 30s
acquire-timeout: 45s
12. Monitoring and Observability
12.1. Metrics
Gateway Metrics:
-
Request rate per route
-
Response time per route
-
Error rate per route
-
Circuit breaker status
-
Connection pool utilization
Endpoint: /management/metrics
Example Metrics:
gateway.requests{route=admin-service,status=200} 1523
gateway.requests{route=admin-service,status=500} 12
gateway.requests.duration{route=admin-service} 145ms