Developer Info
Technical details about this website - a showcase of full-stack development skills with Angular, Spring Boot and modern DevOps practices.
Tech Stack
Frontend
Backend
Database
DevOps
Documentation
Monitoring
Security
Architecture
Framework: Angular 22
Features:
- Standalone components
- Angular Material UI components
- 3-language i18n (Transloco)
- Reactive forms
- HTTP client
- Router
- Sentry error tracking
- Web Vitals performance monitoring
- Bootstrap 5 UI
- TypeScript 6
Structure: Component-based architecture with services
Framework: Spring Boot 4.1 (Java 21)
Features:
- REST API
- JPA/Hibernate
- Spring Security
- Rate limiting (Bucket4j)
- Email service (Brevo SMTP)
- Sentry error tracking
- Flyway migrations
- Springdoc OpenAPI
- OWASP Encoder
Structure: Layered architecture: Controller → Service → Repository → Entity
Type: PostgreSQL 18
Features:
- JPA entities
- Repository pattern
- Flyway database migrations
- Version-controlled schema
- Production-ready migration management
Containers:
- Frontend (Nginx)
- Backend (Spring Boot 4.1)
- Database (PostgreSQL 18)
Orchestration: Docker Compose
API Documentation
All available API endpoints with details, request/response examples and rate limiting information.
Base URL
- Development:
http://localhost:8080/api - Production:
https://fuber-technics.be/api
Note: In Docker deployment, <code>/api/</code> requests are proxied by the frontend nginx container to the backend container internally. Direct backend access (<code>localhost:8080</code>) only works if the backend port is exposed (development only). In production, all API calls go through the frontend nginx or reverse proxy.
Authentication
All endpoints are publicly accessible. No authentication required.
Rate Limiting
The /contact endpoint has rate limiting: 5 requests per minute, 20 requests per hour per IP address.
Method:GET
Path:/api/ping
Description: Health check endpoint voor monitoring en load balancers.
Success Response:
{
"status": "ok"
}Example:
cURL:
curl -X GET http://localhost:8080/api/pingJavaScript:
const response = await fetch('http://localhost:8080/api/ping');
const data = await response.json();Method:POST
Path:/api/contact
Description: Verstuurt een contact bericht. Rate limited: 5/min, 20/hour per IP.
Request Body:
{
"name": "string (required, max 100)",
"email": "string (required, valid email)",
"phone": "string (optional, max 20)",
"subject": "string (required, max 200)",
"message": "string (required, max 5000)"
}Success Response (201):
{
"id": 1,
"message": "Bericht succesvol verzonden",
"status": "success"
}Error Responses:
400 Bad Request:
{
"error": "Validatiefouten",
"details": [{ "field": "email", "message": "Ongeldig e-mailadres" }]
}429 Too Many Requests:
{
"error": "Te veel verzoeken. Probeer het later opnieuw.",
"retryAfterSeconds": 60
}Example:
cURL:
curl -X POST http://localhost:8080/api/contact \
-H "Content-Type: application/json" \
-d '{"name": "John Doe", "email": "[email protected]", "subject": "Project Inquiry", "message": "I would like to discuss a potential project."}'JavaScript:
const response = await fetch('http://localhost:8080/api/contact', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'John Doe',
email: '[email protected]',
subject: 'Project Inquiry',
message: 'I would like to discuss a potential project.'
})
});
const data = await response.json();Method:GET
Path:/api/projects
Description: Haalt alle projecten op uit het portfolio, gesorteerd op aanmaakdatum (nieuwste eerst).
Query Parameters:
page- integer (optional, default: 0): Pagina nummer (0-indexed)size- integer (optional): Aantal items per pagina. Als niet opgegeven, worden alle resultaten teruggegeven.
Success Response:
[{
"id": 1,
"title": "E-Commerce Platform",
"type": "Web Application",
"stack": "Spring Boot, Angular, PostgreSQL",
"role": "Full Stack Developer",
"highlights": ["Implemented payment integration"],
"githubUrl": "https://github.com/example/project",
"demoUrl": "https://demo.example.com",
"description": "A full-featured e-commerce platform",
"createdAt": "2024-01-15T10:00:00",
"updatedAt": "2024-01-20T14:30:00"
}]Example:
cURL:
curl -X GET http://localhost:8080/api/projectsJavaScript:
const projects = await fetch('http://localhost:8080/api/projects')
.then(res => res.json());Method:GET
Path:/api/skills
Description: Haalt technische skills op. Kan gefilterd worden op categorie.
Query Parameters:
category- string (optional): Filter op categorie. Mogelijke waarden: backend, frontend, databases, devops, toolspage- integer (optional, default: 0): Pagina nummer (alleen gebruikt als category niet opgegeven is)size- integer (optional): Aantal items per pagina (alleen gebruikt als category niet opgegeven is)
Success Response:
[{
{ "id": 1, "name": "Java", "category": "backend", "orderIndex": 1 },
{ "id": 2, "name": "Spring Boot", "category": "backend", "orderIndex": 2 }
}]Example:
cURL:
curl -X GET "http://localhost:8080/api/skills?category=backend"JavaScript:
const backendSkills = await fetch(
'http://localhost:8080/api/skills?category=backend'
).then(res => res.json());Method:GET
Path:/api/postal-codes/{postalCode}
Description: Zoekt de stad op voor een Belgische postcode.
Path Parameters:
postalCode- string (required): 4-cijferige Belgische postcode
Success Response:
{
"postalCode": "1000",
"city": "Brussel"
}Error Responses (400):
{
"error": "Invalid postal code format",
"message": "Postal code must be 4 digits"
}Example:
cURL:
curl -X GET http://localhost:8080/api/postal-codes/1000JavaScript:
const postalCode = '1000';
const result = await fetch(
`http://localhost:8080/api/postal-codes/${postalCode}`
).then(res => res.json());Method:GET
Path:/api/postal-codes/{postalCode}/validate
Description: Valideert of een postcode bestaat in België.
Path Parameters:
postalCode- string (required): 4-cijferige Belgische postcode
Success Response:
{
"postalCode": "1000",
"valid": true
}Example:
cURL:
curl -X GET http://localhost:8080/api/postal-codes/1000/validateJavaScript:
const postalCode = '1000';
const result = await fetch(
`http://localhost:8080/api/postal-codes/${postalCode}/validate`
).then(res => res.json());Security Features & Code Quality
- CORS configuration with allowed origins
- CSRF protection (disabled for public API endpoints)
- Rate limiting (5 req/min, 20 req/hour per IP)
- Input validation and sanitization (OWASP Encoder)
- XSS protection
- SQL injection protection via JPA
- Security headers (CSP, X-Frame-Options)
- Error tracking and monitoring (Sentry) for production debugging
- Error handling with proper HTTP status codes
- DTO/Entity separation
- Input validation (Bean Validation)
- Structured logging
- Dependency locking for security (Gradle lock mode: STRICT)
- Database migrations with Flyway (version control, rollback support)
- Clean code principles
- Production error tracking with Sentry (frontend & backend)
- Performance monitoring (no session replay)
- Code quality tools: Checkstyle, SpotBugs
- Test coverage: JaCoCo (50% target)
Testing
Framework: JUnit 5 + Mockito
Tools:
- JUnit 5
- Mockito
- Spring Boot Test
- MockMvc
- H2 in-memory database
- Spring Security Test
Coverage: JaCoCo (50% target)
Test Types:
- Unit tests
- Integration tests
Framework: Jest + Cypress
Tools:
- Jest
- Cypress E2E
- Angular Testing Utilities
- HttpTestingController
- jest-preset-angular
Coverage: Jest coverage reports
Test Types:
- Unit tests (services, components)
- E2E tests (Cypress)
Test Commands
./test-all.sh (Unix) or test-all.bat (Windows)- Run all tests (backend, frontend unit, E2E)./gradlew test- Backend testsnpm test- Frontend unit testsnpm run e2e- E2E tests (Cypress)