TypeScript, Angular 21, NgRx 21, Bootstrap 5, Java, Spring Boot 3, Spring for GraphQL, Spring Security (JWT), Spring Data JPA, PostgreSQL
This example contains a frontend and backend:
- The frontend is an Angular 21 application using Bootstrap 5 for view designs, with NgRx for state management.
- The backend is a GraphQL API built with Spring Boot 3 providing the ability to create, delete, and list reservations plus available rooms.
This is a Java/Angular mirror of the React/Python version of the same application.
Context:
- When a room is reserved, it cannot be reserved by another guest on overlapping dates.
- Check-in date must be in the future.
- Final price for reservations is determined by daily price * number of nights, plus the cleaning fee.
Web UI Usage:
API Usage:
Example usage via curl:
# First, grab an access token provided by the API
ACCESS_TOKEN=$(curl -s -X POST \
-H 'accept: application/json' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=password&username=example-user&password=example-user' \
"http://localhost:$RESERVATION_PORT/development/token" | jq -r '.access_token')
# List all existing booked reservations
curl http://localhost:$RESERVATION_PORT/graphql \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-d '{"query": "query { getAllReservations { reservations { room_id checkin_date checkout_date } } }"}'
# Create a new reservation
curl http://localhost:$RESERVATION_PORT/graphql \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-d '{"query": "mutation { createReservation(input: { room_id: \"room_1\", checkin_date: \"2026-12-31\", checkout_date: \"2027-01-02\" }) { success errors reservations { id room_id checkin_date checkout_date total_charge } } }"}'
# Delete a reservation
curl http://localhost:$RESERVATION_PORT/graphql \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-d '{"query": "mutation { deleteReservation(reservationId: 1) { success errors } }"}'GraphiQL Playground:
Navigate to http://localhost:8080/graphiql when the backend is running.
To run the service, you will need to install the following tools:
The below are optional but highly recommended:
- nvm: manages NodeJS versions
- Direnv: manages environment variables
- SDKMAN: manages Java/Gradle versions
A .envrc file is included with sensible defaults for local development. If you use Direnv, it will be loaded automatically. Otherwise source it manually:
source .envrcKey variables:
| Variable | Description | Default |
|---|---|---|
SECRET_KEY |
JWT signing secret | s3cr3t-k3y |
RESERVATION_PORT |
Backend server port | 8080 |
ALLOWED_ORIGINS |
CORS allowed origins | http://localhost:3000 |
PG_URL |
Full PostgreSQL connection URL | constructed from PG_* vars |
PG_HOST |
PostgreSQL host | localhost |
PG_PORT |
PostgreSQL port (Docker mapped) | 8081 |
PG_NAME |
PostgreSQL database name | hotel_development |
PG_USER |
PostgreSQL username | postgres |
PG_PASSWD |
PostgreSQL password | postgres |
npm install # root (installs concurrently, husky)
cd frontend && npm install && cd ..docker-compose up -dThis starts a PostgreSQL 15 instance on port 8081. On first backend startup, Spring JPA will create the schema and seed rooms via data.sql.
npm run devThis starts both services concurrently:
- Backend →
http://localhost:8080 - Frontend →
http://localhost:3000
The frontend proxies /graphql and /development requests to the backend automatically.
The backend uses JUnit 5 with @SpringBootTest and an H2 in-memory database:
cd backend
./gradlew testThe frontend uses Karma + Jasmine:
cd frontend
npm test # interactive (watch mode)
npm run test:ci # single run (for CI)Angular major version upgrades require ng update rather than a plain npm install, because each major ships migration schematics that update your source code automatically. Upgrade one major at a time (e.g. 21 to 22, not 21 to 23):
cd frontend
npx ng update @angular/core@22 @angular/cli@22 @ngrx/store@22Dependabot is configured to ignore Angular major version bumps for this reason. Run the upgrade manually when ready.
The backend uses Checkstyle for linting and Spotless with Google Java Format for formatting:
cd backend
./gradlew lint # checkstyle + verify formatting
./gradlew format # auto-format source codeThe frontend uses ESLint with Angular ESLint for linting and Prettier for formatting:
cd frontend
npm run lint # ESLint across all .ts and .html files
npm run format # Prettier across all .ts, .html, and .scss filesThis repository includes two stub components as learning exercises:
-
Exercise #1:
frontend/src/app/screens/reservations/show/show-reservation.component.tsImplement the reservation detail view. -
Exercise #2:
frontend/src/app/screens/reservations/edit/edit-reservation.component.tsImplement the reservation edit form (may require backend changes for an update mutation).
.
├── backend/ # Spring Boot 3 application
│ ├── build.gradle
│ ├── config/checkstyle/
│ │ └── checkstyle.xml # Checkstyle rules (Google style)
│ ├── src/main/java/com/acme/hotel/
│ │ ├── HotelApplication.java
│ │ ├── auth/ # JWT authentication
│ │ ├── config/ # Spring Security configuration
│ │ ├── graphql/ # GraphQL resolvers
│ │ ├── model/ # JPA entities
│ │ ├── repository/ # Spring Data JPA repositories
│ │ └── service/ # Business logic
│ └── src/main/resources/
│ ├── application.properties
│ ├── data.sql # Room seed data
│ └── graphql/schema.graphqls
├── frontend/ # Angular 21 application
│ ├── .eslintrc.json # ESLint + Angular ESLint rules
│ ├── .prettierrc # Prettier formatting config
│ ├── src/app/
│ │ ├── core/ # Auth service + HTTP interceptor
│ │ ├── graphql/ # GraphQL queries and mutations
│ │ ├── screens/ # Page components
│ │ └── store/ # NgRx state management
│ └── proxy.conf.json # Dev proxy to backend
├── tools/
│ └── env.example.sh # Example environment variables
├── docker-compose.yml # PostgreSQL via Docker
└── .github/workflows/
└── pr-validate.yml # CI: backend + frontend tests
