|
2 | 2 |
|
3 | 3 |  |
4 | 4 |
|
5 | | -REST API CRUD application built with Spring Boot 4 and PostgreSQL. |
| 5 | +Educational project: REST API for managing books built with Spring Boot 4 and PostgreSQL. |
6 | 6 |
|
7 | | -## Tech Stack |
| 7 | +## What You Will Learn |
8 | 8 |
|
9 | | -- Java 17 |
10 | | -- Spring Boot 4.0.2 |
11 | | -- Spring Data JPA |
12 | | -- Spring Security (Basic Auth) |
13 | | -- PostgreSQL |
14 | | -- Lombok |
15 | | -- Docker |
| 9 | +- Building REST API with Spring Boot |
| 10 | +- Working with databases using Spring Data JPA |
| 11 | +- Data validation |
| 12 | +- Error handling (RFC 7807 Problem Details) |
| 13 | +- Basic Authentication |
| 14 | +- Swagger/OpenAPI documentation |
| 15 | +- Docker for database |
| 16 | +- Writing tests |
16 | 17 |
|
17 | 18 | ## Requirements |
18 | 19 |
|
19 | | -- JDK 17+ |
20 | | -- Maven 3.8+ |
21 | | -- PostgreSQL 14+ (or Docker) |
| 20 | +Before starting, make sure you have installed: |
22 | 21 |
|
23 | | -## Quick Start |
| 22 | +- **Java 17+** — check: `java -version` |
| 23 | +- **Maven 3.8+** — check: `mvn -version` |
| 24 | +- **Docker** — check: `docker --version` |
24 | 25 |
|
25 | | -### 1. Start PostgreSQL |
| 26 | +## Quick Start (5 minutes) |
| 27 | + |
| 28 | +### Step 1: Clone the repository |
| 29 | + |
| 30 | +```bash |
| 31 | +git clone https://github.com/OKaluzny/spring-boot-rest-api-postgresql.git |
| 32 | +cd spring-boot-rest-api-postgresql |
| 33 | +``` |
| 34 | + |
| 35 | +### Step 2: Start PostgreSQL |
26 | 36 |
|
27 | 37 | ```bash |
28 | 38 | docker-compose up -d postgres |
29 | 39 | ``` |
30 | 40 |
|
31 | | -### 2. Run Application |
| 41 | +Verify the container is running: |
| 42 | +```bash |
| 43 | +docker ps |
| 44 | +``` |
| 45 | +You should see container `book-db` with status `Up`. |
| 46 | + |
| 47 | +### Step 3: Run the application |
32 | 48 |
|
33 | 49 | ```bash |
34 | 50 | mvn spring-boot:run |
35 | 51 | ``` |
36 | 52 |
|
37 | | -Application starts at `http://localhost:8080` |
| 53 | +Wait for the message: |
| 54 | +``` |
| 55 | +Started Application in X seconds |
| 56 | +``` |
| 57 | + |
| 58 | +### Step 4: Open Swagger UI |
38 | 59 |
|
39 | | -### 3. Default Credentials |
| 60 | +Open in browser: **http://localhost:8080/swagger-ui.html** |
40 | 61 |
|
41 | | -- **API Auth:** `user:user` |
42 | | -- **Database:** `postgres:postgres` |
| 62 | +Here you can interactively test the API. |
43 | 63 |
|
44 | | -## API Endpoints |
| 64 | +### Step 5: Authorization |
| 65 | + |
| 66 | +API requires authentication: |
| 67 | +- **Username:** `user` |
| 68 | +- **Password:** `user` |
45 | 69 |
|
46 | | -| Method | Endpoint | Description | |
47 | | -|--------|----------|-------------| |
48 | | -| POST | `/api/books` | Create book | |
49 | | -| GET | `/api/books` | Get all books | |
50 | | -| GET | `/api/books/{id}` | Get book by ID | |
51 | | -| GET | `/api/books?name={name}` | Search by name | |
52 | | -| PUT | `/api/books/{id}` | Update book | |
53 | | -| DELETE | `/api/books/{id}` | Delete book | |
54 | | -| DELETE | `/api/books` | Delete all books | |
| 70 | +In Swagger UI click the **"Authorize"** button and enter these credentials. |
55 | 71 |
|
56 | | -### Example Request |
| 72 | +## Your First Request with curl |
| 73 | + |
| 74 | +Create a book: |
57 | 75 |
|
58 | 76 | ```bash |
59 | 77 | curl -X POST http://localhost:8080/api/books \ |
60 | 78 | -H "Content-Type: application/json" \ |
61 | | - -H "Authorization: Basic dXNlcjp1c2Vy" \ |
62 | | - -d '{ |
63 | | - "name": "Java Programming", |
64 | | - "description": "Learn Java", |
65 | | - "tags": ["java", "programming"] |
66 | | - }' |
| 79 | + -u user:user \ |
| 80 | + -d '{"name": "Java Programming", "description": "Learn Java", "tags": ["java"]}' |
67 | 81 | ``` |
68 | 82 |
|
69 | | -### Example Response |
70 | | - |
| 83 | +Response: |
71 | 84 | ```json |
72 | 85 | { |
73 | 86 | "id": 1, |
74 | 87 | "name": "Java Programming", |
75 | 88 | "description": "Learn Java", |
76 | | - "tags": ["java", "programming"] |
| 89 | + "tags": ["java"] |
77 | 90 | } |
78 | 91 | ``` |
79 | 92 |
|
80 | | -## Configuration |
| 93 | +Get all books: |
| 94 | +```bash |
| 95 | +curl http://localhost:8080/api/books -u user:user |
| 96 | +``` |
| 97 | + |
| 98 | +## API Endpoints |
| 99 | + |
| 100 | +| Method | URL | Description | |
| 101 | +|--------|-----|-------------| |
| 102 | +| `POST` | `/api/books` | Create a book | |
| 103 | +| `GET` | `/api/books` | Get all books (paginated) | |
| 104 | +| `GET` | `/api/books/{id}` | Get book by ID | |
| 105 | +| `GET` | `/api/books?name=Java` | Search by name | |
| 106 | +| `PUT` | `/api/books/{id}` | Update a book | |
| 107 | +| `DELETE` | `/api/books/{id}` | Delete a book | |
81 | 108 |
|
82 | | -Environment variables: |
| 109 | +### Pagination |
83 | 110 |
|
84 | | -| Variable | Default | Description | |
85 | | -|----------|---------|-------------| |
86 | | -| `DB_HOST` | localhost | Database host | |
87 | | -| `DB_PORT` | 5432 | Database port | |
88 | | -| `DB_NAME` | book_db | Database name | |
89 | | -| `DB_USERNAME` | postgres | Database user | |
90 | | -| `DB_PASSWORD` | postgres | Database password | |
91 | | -| `SECURITY_USER` | user | API username | |
92 | | -| `SECURITY_PASSWORD` | user | API password | |
93 | | -| `SERVER_PORT` | 8080 | Application port | |
| 111 | +``` |
| 112 | +GET /api/books?page=0&size=10&sort=name,asc |
| 113 | +``` |
94 | 114 |
|
95 | | -## Docker |
| 115 | +- `page` — page number (starting from 0) |
| 116 | +- `size` — number of items per page |
| 117 | +- `sort` — sorting (field,direction) |
96 | 118 |
|
97 | | -### Run full stack |
| 119 | +## Project Structure |
98 | 120 |
|
99 | | -```bash |
100 | | -docker-compose --profile full up -d |
| 121 | +``` |
| 122 | +src/main/java/com/kaluzny/ |
| 123 | +│ |
| 124 | +├── Application.java # Entry point |
| 125 | +│ |
| 126 | +├── config/ # Configuration |
| 127 | +│ ├── SecurityConfig.java # Security settings |
| 128 | +│ └── OpenApiConfig.java # Swagger settings |
| 129 | +│ |
| 130 | +├── domain/ # Data layer |
| 131 | +│ ├── Book.java # JPA entity |
| 132 | +│ └── BookRepository.java # Repository |
| 133 | +│ |
| 134 | +├── dto/ # Data Transfer Objects |
| 135 | +│ ├── BookCreateRequest.java |
| 136 | +│ ├── BookUpdateRequest.java |
| 137 | +│ ├── BookResponse.java |
| 138 | +│ └── BookMapper.java |
| 139 | +│ |
| 140 | +├── service/ # Business logic |
| 141 | +│ └── BookService.java |
| 142 | +│ |
| 143 | +├── exception/ # Error handling |
| 144 | +│ ├── BookNotFoundException.java |
| 145 | +│ └── GlobalExceptionHandler.java |
| 146 | +│ |
| 147 | +└── web/ # REST controllers |
| 148 | + └── BookRestController.java |
| 149 | +``` |
| 150 | + |
| 151 | +## Architecture |
| 152 | + |
| 153 | +``` |
| 154 | +HTTP Request |
| 155 | + ↓ |
| 156 | +┌─────────────────┐ |
| 157 | +│ Controller │ ← Receives DTO, validation |
| 158 | +└────────┬────────┘ |
| 159 | + ↓ |
| 160 | +┌─────────────────┐ |
| 161 | +│ Service │ ← Business logic |
| 162 | +└────────┬────────┘ |
| 163 | + ↓ |
| 164 | +┌─────────────────┐ |
| 165 | +│ Repository │ ← Database operations |
| 166 | +└────────┬────────┘ |
| 167 | + ↓ |
| 168 | +┌─────────────────┐ |
| 169 | +│ PostgreSQL │ |
| 170 | +└─────────────────┘ |
101 | 171 | ``` |
102 | 172 |
|
103 | | -### Run only PostgreSQL |
| 173 | +## Stopping the Application |
104 | 174 |
|
| 175 | +1. Stop Spring Boot: `Ctrl+C` in terminal |
| 176 | +2. Stop PostgreSQL: |
105 | 177 | ```bash |
106 | | -docker-compose up -d postgres |
| 178 | +docker-compose down |
107 | 179 | ``` |
108 | 180 |
|
109 | | -### Build image only |
| 181 | +## Running Tests |
110 | 182 |
|
111 | 183 | ```bash |
112 | | -docker build -t book-api . |
| 184 | +mvn test |
113 | 185 | ``` |
114 | 186 |
|
115 | | -## Actuator Endpoints |
| 187 | +Tests use H2 in-memory database, PostgreSQL is not required. |
116 | 188 |
|
117 | | -- Health: `GET /actuator/health` |
118 | | -- Info: `GET /actuator/info` |
119 | | -- Metrics: `GET /actuator/metrics` |
| 189 | +## Useful Links |
120 | 190 |
|
121 | | -## Project Structure |
| 191 | +| URL | Description | |
| 192 | +|-----|-------------| |
| 193 | +| http://localhost:8080/swagger-ui.html | Swagger UI | |
| 194 | +| http://localhost:8080/v3/api-docs | OpenAPI JSON | |
| 195 | +| http://localhost:8080/actuator/health | Health check | |
122 | 196 |
|
123 | | -``` |
124 | | -src/main/java/com/kaluzny/ |
125 | | -├── Application.java # Entry point |
126 | | -├── domain/ |
127 | | -│ ├── Book.java # JPA Entity |
128 | | -│ └── BookRepository.java # Spring Data Repository |
129 | | -├── exception/ |
130 | | -│ ├── BookNotFoundException.java |
131 | | -│ └── GlobalExceptionHandler.java |
132 | | -└── web/ |
133 | | - └── BookRestController.java # REST Controller |
134 | | -``` |
| 197 | +## Troubleshooting |
135 | 198 |
|
136 | | -## Testing |
| 199 | +### Error: "Port 8080 already in use" |
137 | 200 |
|
| 201 | +Find and stop the process: |
138 | 202 | ```bash |
139 | | -# Run tests (requires PostgreSQL) |
140 | | -mvn test |
| 203 | +lsof -i :8080 |
| 204 | +kill -9 <PID> |
| 205 | +``` |
| 206 | + |
| 207 | +### Error: "Connection refused" to PostgreSQL |
141 | 208 |
|
142 | | -# Skip tests |
143 | | -mvn package -DskipTests |
| 209 | +Check that container is running: |
| 210 | +```bash |
| 211 | +docker ps |
| 212 | +docker-compose up -d postgres |
144 | 213 | ``` |
145 | 214 |
|
| 215 | +### Error: "docker: command not found" |
| 216 | + |
| 217 | +Install Docker Desktop: https://www.docker.com/products/docker-desktop |
| 218 | + |
| 219 | +## Technologies |
| 220 | + |
| 221 | +| Technology | Version | Description | |
| 222 | +|------------|---------|-------------| |
| 223 | +| Java | 17 | Programming language | |
| 224 | +| Spring Boot | 4.0.2 | Framework | |
| 225 | +| Spring Data JPA | - | ORM for database | |
| 226 | +| Spring Security | - | Security | |
| 227 | +| PostgreSQL | 16 | Database | |
| 228 | +| Swagger/OpenAPI | 3.1 | API documentation | |
| 229 | +| Lombok | 1.18 | Reduce boilerplate | |
| 230 | +| JUnit 5 | - | Testing | |
| 231 | + |
146 | 232 | ## License |
147 | 233 |
|
148 | 234 | MIT |
0 commit comments