- The Upgrader: Developers transitioning to the latest Spring Boot and Angular standards.
- The Architect: Teams needing a clean, production-ready baseline for new projects.
- The Minimalist: Anyone who wants a "no-fluff" setup using modern best practices.
This project is a minimal but production-oriented reference application intended to kickstart modern fullstack development.
It demonstrates real-world security concerns, including:
- Session-based authentication
- CSRF / XSRF protection
- CORS vs proxy-based setups between an Angular frontend and a Spring Boot backend
The emphasis is on clarity, correctness, and modern best practices, with sensible defaults and configuration files that include only what is strictly required for the application to run correctly.
In particular, the POM files, .gitignore, .editorconfig, application.yml, application-postgres.yml, angular.json, tsconfig.json, app.config.ts, styles.scss are intentionally kept minimal and explicit.
In other words, there is no superfluous or dead code.
Build tooling is provisioned via the Maven Wrapper and a frontend Maven plugin.
As a result, the only prerequisite to build and run the project is a Java 25 JDK.
Development was done using IntelliJ IDEA Ultimate 2025.
While other IDEs can be used, IntelliJ is recommended, as the repository includes IDE-specific run configurations and database connection files that are referenced in the Development section below.
The project provides a focused set of features commonly required in real-world production systems, available from the outset.
It consists of a parent Maven POM and two child modules:
- an Angular frontend
- a Spring Boot backend
During the build, the frontend Maven plugin packages the Angular application directly into the Spring Boot executable JAR.
The frontend can also be run independently and communicate with the backend, making it possible to explicitly test CORS (cross-origin) and same-origin (proxy-based) configurations.
- Two supported databases, selected via Spring profiles:
default: embedded, file-based H2 (with H2 web console)postgres: PostgreSQL via Docker Compose (with pgAdmin web console)
- Caching of read-only HTTP requests using Caffeine (see EmployeeService.java)
- Consistent, structured error handling using Spring
ProblemDetailand a global@RestControllerAdvice(see GlobalExceptionHandler.java) - Session-based authentication using
JSESSIONID(see SecurityConfig.java and AuthenticationController.java) - HTTP requests handled using virtual threads (see AppInfoController.java)
- REST API (see EmployeeController.java) following proper HTTP semantics (verbs, dto validity, status codes, etc.)
- Actuator endpoints exposed on a separate port
- OpenAPI specification with Swagger UI
- Centralized CORS configuration allowing cross-origin requests from
http://localhost:4200(see application.yml and CorsConfig.java) - CSRF protection enabled for additional security
- SPA fallback routing (see SpaForwardFilter.java) to support client-side routes (frontend-managed 404s)
- Custom logging configuration with separate application and SQL log files (see logback-spring.xml)
- Log level for these files are configured centrally in application.yml
- Each request is tagged with a unique request ID using MDC (see MdcRequestIdFilter.java)
- Comprehensive testing setup:
- Unit tests with Mockito (see EmployeeServiceTest.java)
- MockMvc tests (see EmployeeControllerTest.java)
- Spring Boot integration tests (see AppInfoControllerIT.java and EmployeeServiceIT.java)
- IntelliJ HTTP Requests integration tests (see crud-employees-test.http)
- Clean and predictable directory structure
- TypeScript strict compiler options (see tsconfig.json)
- TypeScript path aliases (
@env,@core,@shared,@features) for readable and maintainable imports (see tsconfig.json) - Angular signals for component-level state management (see employee-list.store.ts)
- Standalone components like employee-list.page.ts that use a dedicated service such as employee-api.service.ts
- Angular Material integration with view transition effects (e.g.
withViewTransitions) for smoother navigation (see app.config.ts) - Optional Angular development proxy to demonstrate CORS vs same-origin setups via environment-based configuration (see environment.ts, environment.proxy.ts, and proxy.conf.json)
- Client-side custom 404 page (see not-found.page.html)
- Automatic restoration of an existing backend session on application startup or page refresh (see
provideAppInitializerin app.config.ts) - Multilingual support (English and French), including UI text and browser tab titles (see app.config.ts and i18n-title-strategy.ts)
- Global HTTP error interceptor (see error.interceptor.ts) that:
- maps backend
ProblemDetailresponses - logs errors to the console
- displays user-friendly messages via snackbars (see notification.service.ts)
- maps backend
- Route guards enforcing authentication and redirecting unauthenticated users to the login page (see auth.guard.ts and app.routes.ts)
- XSRF/CSRF token forwarding via a custom Http interceptor (see xsrf.interceptor.ts) that reads the XSRF-TOKEN cookie and attaches it as X-XSRF-TOKEN only for backend API calls (supports proxy and CORS setups).
It is better to build the project using the Maven Wrapper to ensure that the correct Maven version is used (see maven-wrapper.properties).
Commands on Windows:
cd ./employees-fullstack/ ./mvnw.cmd clean installOn a Linux machine:
./mvnw clean installOr you can use the IntelliJ maven run config:
👉 employees-fullstack [clean,install]
You can run the backend using the IntelliJ Spring Boot run configurations provided in the .idea/runConfigurations folder.
👉 SbBackendApplication [default h2]
👉 SbBackendApplication [postgres]
Test users with username/email/password are defined in:
👉 AppConfig.java
When you log in, you will see a JSESSIONID cookie in the application tab of Chrome DevTools.
When you log out, this cookie is removed.
http://localhost:9090/actuator
Also see 👉 actuator.http
To see the DB caches: http://localhost:9090/actuator/caches
http://localhost:8090/swagger-ui/index.html
http://localhost:8090/api/info/request-thread
You can test the backend by running the IntelliJ httpRequests test suite provided in the .httpRequests folder.
👉 crud-employees-test.http
To use the PostgreSQL run configuration, make sure Docker Desktop is running, then start the containers:
cd ./employees-fullstack/ docker compose up -dThe pgadmin console is available (you have to wait a bit after starting the containers).
To connect to this console, see the credentials in application-postgres.yml and docker-compose.yml, then go to:
http://localhost:5050
To stop the containers:
docker compose downTo stop the containers AND delete all data stored in the postgres-employees-data Docker volume:
docker compose down -vWith the default profile, the data is stored in a h2 db in ./employees-fullstack/data
The h2 console is available here:
http://localhost:8090/h2
cd ./angular-frontend/employees-ui/ ng serveOr you can run the frontend using the IntelliJ run configuration provided in the .idea/runConfigurations folder.
👉 employees-ui [CORS].
This mode makes the browser call the backend at http://localhost:8090 directly from http://localhost:4200, so the origins are different.
You will see real CORS preflight (OPTIONS) requests in the network tab of Chrome DevTools.
If you remove http://localhost:4200 from app.cors.allowed-origins in application.yml,
you will see CORS errors.
ng serve --configuration=proxyOr you can run the frontend using the IntelliJ run configuration provided in the .idea/runConfigurations folder.
👉 employees-ui [start proxy - no CORS].
This mode proxies all /api calls from the Angular dev server to http://localhost:8090,
so the browser never makes cross-origin requests.
You will see no CORS preflight (OPTIONS) requests.
This mirrors the behavior obtained when the application is served directly by the backend at http://localhost:8090 after a Maven build.
http://localhost:8090/api/employees/99
=> backend 404:
{"detail":"Could not find employee with id [=99]","instance":"/api/employees/99","status":404,"title":"Resource Not Found"}
http://localhost:8090/employees/99
=> frontend handles that and returns the custom 404 page from employees-ui
http://localhost:8090/notExist
=> frontend handles that and returns the custom 404 page from employees-ui
