This document captures the key decisions and steps taken to build Qare, along with concise explanations you can refer to during maintenance or demos.
mkdir qare && cd qare
mkdir backend && cd backend
curl https://start.spring.io/starter.zip \
-d name=qare-backend \
-d groupId=com.qare \
-d artifactId=qare-backend \
-d packageName=com.qare.app \
-d language=java \
-d type=maven-project \
-d javaVersion=24 \
-d bootVersion=3.5.5 \
-d dependencies=web,validation,actuator,lombok \
-o starter.zip
unzip starter.zip && rm starter.zipWe’re using JUnit 5 (Jupiter) for tests via
spring-boot-starter-test.
-
CorsConfig
Tells Spring which browser origins are allowed to call the API (CORS).- CORS affects browsers only; tools like
curl/Postman ignore it. - CORS is not authentication. Even with correct CORS, your endpoints are open unless you add auth (e.g., Spring Security + JWT/cookies).
- If you don’t use JWT/auth: all endpoints are publicly reachable. CORS won’t stop malicious sites from their own users calling your API if you rely on cookies—use token-based auth or CSRF protection for cookie-based auth.
- CORS affects browsers only; tools like
-
DBConfig
Handles the database connection and provides CRUD operations. We add dependencies for JDBC and H2.- JDBC (Java Database Connectivity): standard Java API used by drivers (H2/Postgres/etc.) for SQL access.
- H2 (file-based): great for local dev/tests; DB is created on first connection.
- Switching to PostgreSQL for prod is trivial—swap the dependency and datasource URL; the CRUD code still works.
Swap H2 → Postgres in
pom.xml:<!-- remove H2 dependency --> <!-- add Postgres --> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency>
application.ymlfor Postgres:spring: datasource: url: jdbc:postgresql://localhost:5432/qaredb username: postgres password: yourpassword
-
QareController
The app’s HTTP adapter: turns incoming HTTP requests into service calls, then shapes the HTTP responses.Where Jackson fits in (serialization/deserialization):
spring-boot-starter-webpulls inspring-boot-starter-json→ Jackson (jackson-databind, etc.).- Spring MVC registers
MappingJackson2HttpMessageConverterwith a JacksonObjectMapper. - Returning
MedicalSupply(orList<MedicalSupply>) from a@RestControllermethod lets Spring’s message converter call Jackson to serialize to JSON. @RequestBody MedicalSupplydoes the reverse—Jackson deserializes JSON into your model.
Pipeline:
Controller return → MessageConverter → ObjectMapper.writeValue(...) → JSON response
MedicalSupply(record): holds business fields and bean validation annotations, e.g.:public record MedicalSupply( @jakarta.validation.constraints.NotBlank String name, @jakarta.validation.constraints.Min(0) int amount, @jakarta.validation.constraints.NotBlank String unitName ) {}
QareService
Orchestrates operations between controller and DB: input normalization (e.g.,strip()/trim()), CRUD delegation, and (optionally) transactions.
application.yml
Externalizes configuration (port, datasource, logging, actuator). Spring reads it at startup; values can be overridden by env vars/CLI args/profiles.
- Controller tests:
@WebMvcTest(QareController.class)+ mock service; assert status codes (201/200/404/204), headers (Location), JSON bodies, and validation 400s. - DBConfig tests:
@JdbcTest+ H2; verify table creation at startup, CRUD operations, PK violation, and DBCHECK (amount >= 0)constraint. - Service tests: Mockito unit tests; verify normalization and exception propagation (no blanket catching).
- Jupiter is the platform (JUnit 5); AssertJ for fluent assertions.
# at repo root
mkdir frontend && cd frontend
npm create vite@latest . -- --template react-ts
npm installEdit vite.config.ts:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
server: {
port: 5173,
proxy: {
'/api': 'http://localhost:8080', // forward /api to Spring Boot
},
},
})- Model: Type definitions for
Supply(orMedicalSupply). - View: React components & pages (Home, Store, Admin, Search).
SuppliesTableincludes Status column: amount> 10(green),5–10(orange),< 5(red).SupplyFormhandles create/update; in edit mode, thename(key) is immutable.
- Controller: API client (
fetchwrappers) used by pages/components.
├─ backend/ # Spring Boot API
│ ├─ src/main/java/com/qare/app
│ │ ├─ controller/QareController.java
│ │ ├─ service/QareService.java
│ │ ├─ config/{DBConfig, CorsConfig}.java
│ │ └─ model/MedicalSupply.java
│ └─ src/main/resources/application.yml
├─ frontend/ # React UI
│ ├─ src/
│ │ ├─ controller/supplies_client.ts
│ │ ├─ view/
│ │ │ └─ components/{SupplyForm, SupplyTable}.tsx
│ │ │ └─ pages/{Home,Store,Admin,Search}.tsx
│ │ ├─ model/supply_model.ts
│ │ └─ App.tsx
│ └─ vite.config.ts
└─ start.bash # simple dev starter (backend + frontend)
-
Start everything:
chmod +x start.bash ./start.bash
Frontend → http://localhost:5173 • Backend → http://localhost:8080
-
Auto-restart backend (optional): add
spring-boot-devtoolsand enable auto-make/compile in your IDE. -
Switch DB to Postgres: swap dependency + configure datasource; keep CRUD code unchanged.
-
Security (future): add Spring Security; protect Admin routes; JWT for APIs or cookie auth with CSRF protection.
- Create —
POST /api/supplies→201 Created+Locationheader - Read all —
GET /api/supplies→MedicalSupply[] - Read one —
GET /api/supplies/{name}→200or404 - Update —
PUT /api/supplies/{name}→200or404 - Delete —
DELETE /api/supplies/{name}→204or404
-
Why is CORS not security?
CORS only controls which browsers may read responses; it doesn’t prevent direct calls to your API nor authenticate users. Use authentication/authorization for real protection. -
Why H2 for dev?
Fast, simple, no install. You can still test DB constraints and swap to Postgres for staging/prod.