A lightweight full-stack inventory management application for tracking products — view, add, edit, delete, and monitor stock status.
docker compose up --build
All three services start automatically. Open http://localhost:3000 in your browser.
| Service | URL |
|---|---|
| Frontend | http://localhost:3000 |
| Backend | http://localhost:4000 |
| Database | localhost:5432 |
The system is composed of three Docker services orchestrated with Docker Compose. The React + Vite frontend is a single-page app served by nginx that communicates with the Node.js + Express backend over HTTP. The backend uses TypeORM to manage a PostgreSQL 16 database, with synchronize: true ensuring the schema is created automatically on startup. Product status (in_stock, low_stock, out_of_stock) is derived from quantity using TypeORM entity lifecycle hooks and persisted to the database on every create or update. A postgres healthcheck ensures the backend waits for the database to be ready before connecting.
| Layer | Technology |
|---|---|
| Frontend | React 19, Vite 5, plain CSS |
| Backend | Node.js 25, Express 4, TypeScript 5 |
| ORM | TypeORM 0.3 |
| Database | PostgreSQL 16 |
| Container | Docker + Docker Compose |
Base URL: http://localhost:4000
| Method | Path | Description |
|---|---|---|
| GET | /products | List all products |
| GET | /products/:id | Get a single product |
| POST | /products | Create a new product |
| PATCH | /products/:id | Update a product |
| DELETE | /products/:id | Delete a product |
Validation errors return 400 Bad Request with { "error": "..." }.
Single table products:
| Column | Type | Notes |
|---|---|---|
| id | UUID | Auto-generated primary key |
| name | VARCHAR | Required |
| quantity | INTEGER | Non-negative |
| price | DECIMAL(10,2) | Non-negative |
| status | VARCHAR | Derived from quantity |
| description | TEXT | Optional |
| createdAt | TIMESTAMP | Auto-set on insert |
| updatedAt | TIMESTAMP | Auto-updated on change |
Status values: in_stock (qty > 5), low_stock (qty 1–5), out_of_stock (qty = 0).
Data is stored in a named Docker volume (pgdata) and persists across container restarts.
Three services defined in docker-compose.yml:
- postgres — PostgreSQL 16, persists data to the
pgdatanamed volume, exposes a healthcheck - backend — custom Node.js image, waits for postgres healthcheck before starting
- frontend — multi-stage build (Node builds the Vite bundle, nginx serves it)
- View product list
- Add product (inline form)
- Edit product (modal form)
- Delete product (with confirmation dialog)
- Product status badge (colored by stock level)
- Backend validation (name required, quantity/price non-negative)
- Loading and error states in the UI
- Data persistence via Docker volume
- Full Docker Compose setup (
docker compose up --build)
- The
VITE_API_URLbuild argument is baked into the frontend bundle at Docker build time and defaults tohttp://localhost:4000. If you deploy to a remote host you must rebuild the frontend image with the correct URL. - TypeORM runs with
synchronize: true— suitable for development but should be replaced with explicit migrations before production use. - No authentication is implemented; all endpoints are publicly accessible.
- AI tool used: Claude (Claude Code CLI)
- What I used AI for: Scaffolding the entire project from a product brief — generating all backend TypeScript, frontend React components, hooks, API layer, Docker configuration, and this README.
- What I changed manually/what did I tell AI to improve or correct: Reviewed and adjusted the plan (PostgreSQL version from 15 to 16, confirmed starting with README/.gitignore first), verified the component hierarchy matched the brief, adjusted the color pallete, refectured UI components, refactored popup dialogues behaviour.