A full-stack nutrition and meal-logging application built with Angular, Spring Boot, PostgreSQL, and Docker.
- Frontend: Angular 18 (standalone components)
- Backend: Spring Boot 4.1
- Database: PostgreSQL
- Auth: JWT (JSON Web Tokens)
- Containerization: Docker Compose
- User registration and login (JWT-based authentication)
- Food search (public) and custom food creation (authenticated)
- Meal logging with daily calorie/nutrient totals
- User profile page for viewing and updating daily calorie goals
- Persistent navigation bar (Dashboard, Food Search, Profile) that only appears when logged in, with logout support
- Docker Desktop
- Node.js 20.19+ or 22.12+ (for local frontend development outside Docker)
- Java 17+ and Maven (for local backend development outside Docker)
nutrition-tracker/
├── backend/ # Spring Boot API
│ └── src/main/java/com/nutritiontracker/
│ ├── config/ # Security config, CORS, bean config (PasswordEncoder, AuthenticationManager)
│ ├── controller/ # REST controllers
│ ├── dto/ # Request/response DTOs
│ ├── entity/ # JPA entities
│ ├── repository/ # Spring Data repositories
│ ├── security/ # JWT filter/util
│ └── service/ # Business logic
├── frontend/ # Angular application
│ └── src/app/
│ ├── core/
│ │ └── guards/ # Route guards (authGuard)
│ └── features/ # Feature modules (auth, dashboard, food-search, profile)
├── db/ # Database init scripts
└── docker-compose.yml
docker compose up --build- Frontend: http://localhost:4200
- Backend API: http://localhost:8080
To force a clean rebuild (recommended after pulling changes or editing Dockerfiles):
docker compose down
docker compose build --no-cache
docker compose upStop everything:
docker compose downStop and wipe the database volume:
docker compose down -vBackend:
cd backend
mvn spring-boot:runFrontend:
cd frontend
npm install
ng serveFrontend runs at http://localhost:4200, backend at http://localhost:8080.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /api/auth/register |
No | Register new user |
| POST | /api/auth/login |
No | Login, returns JWT |
| GET | /api/foods/search?query= |
No | Search foods |
| GET | /api/foods/{id} |
Yes | Get food by ID |
| POST | /api/foods |
Yes | Create custom food |
| GET | /api/users/profile |
Yes | Get current user's profile |
| PUT | /api/users/profile |
Yes | Update daily calorie goal |
| POST | /api/meal-logs |
Yes | Log a meal |
| GET | /api/meal-logs/daily?date= |
Yes | Get daily log |
| GET | /api/meal-logs/calories?date= |
Yes | Get daily calorie total |
| DELETE | /api/meal-logs/{id} |
Yes | Delete meal log entry |
Authenticated endpoints require an Authorization: Bearer <token> header, using the JWT returned from /api/auth/login.
# Register
curl -X POST http://localhost:8080/api/auth/register \
-H "Content-Type: application/json" \
-d '{"username":"testuser","email":"test@example.com","password":"password123","dailyCalorieGoal":2000}'
# Login
curl -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"testuser","password":"password123"}'
# Get profile (authenticated)
curl http://localhost:8080/api/users/profile \
-H "Authorization: Bearer YOUR_JWT_HERE"
# Update calorie goal (authenticated)
curl -X PUT http://localhost:8080/api/users/profile \
-H "Authorization: Bearer YOUR_JWT_HERE" \
-H "Content-Type: application/json" \
-d '{"dailyCalorieGoal": 2200}'docker compose ps # list running services
docker compose logs -f # tail all logs
docker compose logs -f frontend # tail frontend logs only
docker compose build frontend --no-cache # rebuild one service cleannpm install # install dependencies
npm ci # clean install from lockfile
ng serve # run dev server
npm run build -- --configuration production # production build (check errors locally before Docker)
ng generate component features/<name> --standalonemvn spring-boot:run # run the app
mvn compile # compile only, fast error check
mvn clean install # full clean build- CORS is currently configured to allow
http://localhost:4200explicitly (seeSecurityConfig.java). Update the allowed origin(s) before deploying beyond local development. PasswordEncoderandAuthenticationManagerbeans live in their own config classes (PasswordEncoderConfig.java,AuthenticationManagerConfig.java) rather than insideSecurityConfig, to avoid a circular bean dependency withJwtFilterandUserService.- The frontend Docker image uses
node:20-alpine; Angular CLI requires Node 20.19+/22.12+ to build. - JWTs are stored in
localStorageon the frontend and attached via an HTTP interceptor. - The navigation bar in the app shell (
app.html/app.ts) is shown only when a JWT is present inlocalStorage, and hides/shows immediately on login and logout without a page refresh.
- Login/register form submission currently has a reported issue where no request fires on click — under investigation.