Masoud Hamad

Lab 6 — Students API (reference implementation)

Complete, runnable Spring Boot reference for Lab 6: Students CRUD REST API. Use this to teach from in class, demo to students, and as a yardstick when grading.

Stack

Layer Library
Language Java 21
Build Maven (Spring Boot 3.3.4)
Web Spring Web (Tomcat)
Persistence Spring Data JPA + Hibernate
Database PostgreSQL 16 (via Docker)
Validation Jakarta Bean Validation (Hibernate Validator)
Docs springdoc-openapi (Swagger UI)
Test JUnit 5 + Spring Boot Test + MockMvc

Quick start

1. Start Postgres

docker compose up -d        # starts postgres:16-alpine on :5432
docker compose ps           # confirm "healthy"

2. Run the app

mvn spring-boot:run

The app boots on http://localhost:8080 and seeds 3 students on first run.

What URL
API root http://localhost:8080/api/students
Swagger UI http://localhost:8080/swagger-ui.html
OpenAPI JSON http://localhost:8080/v3/api-docs

3. Stop everything

# stop the app: Ctrl+C
docker compose down         # stop Postgres
docker compose down -v      # ...and erase its data

Connecting to the DB directly

docker exec -it students-api-db psql -U studentsapi -d studentsdb
# inside psql:
\dt              # list tables
SELECT * FROM students;
\q

Try the API

Open requests.http in VS Code (REST Client extension). Each ### block sends one request.

Or with curl:

curl -s http://localhost:8080/api/students | jq

curl -s -X POST http://localhost:8080/api/students \
     -H 'Content-Type: application/json' \
     -d '{"name":"Test","email":"t@x.com","course":"BCS","year":2}' | jq

Endpoint summary

Method URL Body Success Errors
GET /api/students 200
GET /api/students?course=BCS 200
GET /api/students/{id} 200 404
POST /api/students StudentCreate 201 400, 409
PUT /api/students/{id} StudentUpdate 200 400, 404, 409
DELETE /api/students/{id} 204 404

Project layout

src/main/java/tz/ac/suza/wt/studentsapi/
├── StudentsApiApplication.java   # @SpringBootApplication entry point
├── config/
│   └── DataSeeder.java           # seeds 3 demo students on startup
├── controller/
│   └── StudentController.java    # HTTP layer (thin — no business logic)
├── dto/
│   ├── StudentCreateDto.java     # incoming JSON for POST
│   ├── StudentUpdateDto.java     # incoming JSON for PUT
│   └── StudentResponseDto.java   # outgoing JSON to clients
├── entity/
│   └── Student.java              # JPA @Entity → "students" table
├── exception/
│   ├── EmailAlreadyUsedException.java
│   ├── GlobalExceptionHandler.java   # @RestControllerAdvice + ProblemDetail
│   └── StudentNotFoundException.java
├── repository/
│   └── StudentRepository.java    # extends JpaRepository
└── service/
    └── StudentService.java       # business logic + transactions

docker-compose.yml                # Postgres for local development

Production config

Override the datasource via environment variables — no code changes:

export SPRING_DATASOURCE_URL=jdbc:postgresql://prod.example.com:5432/studentsdb
export SPRING_DATASOURCE_USERNAME=appuser
export SPRING_DATASOURCE_PASSWORD=$(cat /run/secrets/db_password)

For production schema management, replace spring.jpa.hibernate.ddl-auto=update with validate and add Flyway or Liquibase migrations.

Teaching aid

See lab06_walkthrough.md — the step-by-step lecturer guide that builds this same project from start.spring.io.