A RESTful API for managing warehouse inventory. Supports tracking items, stock levels across multiple storage locations, and stock movement history. Built for internal use by warehouse staff and managers, using role-based access to control what each user can do.
- User registration and login with JWT auth
- Create and manage items (SKU, name, description, quantity)
- Create and manage locations (bays, shelves, etc)
- Move stock between locations
- View current stock levels per item and per location
- Role-based access:
worker,manager, andadminroles with different permissions
- Go: HTTP server (
net/http) - PostgreSQL: persistent storage
- goose: database migrations
- sqlc: type-safe SQL query generation
- Docker: containerize the app and database together
After countless hours spent looking everywhere for material, and being told that "the system shows it was delivered" I had enough. I made this with the intention of easily being able to keep track of what material is where while on the job site.
- Clone the repo
git clone https://github.com/LunarDrift/warehouse-api
cd warehouse-api- Create a
.env.dockerfile in the project root with the following variables:
DB_URL=postgres://postgres:postgres@db:5432/warehouse?sslmode=disable
JWT_SECRET=your-secret-here
- Start the app
docker compose up --buildThe server will be available at http://localhost:8080. Migrations run automatically on startup.
Postgres may take a few seconds to initialize on the first run, and the app may try to connect before it's ready causing a connection error. Just try docker compose up again - Postgres should already be running from the first attempt and the app should connect fine.
Stopping the app
docker compose downTo remove the database volume as well (this deletes all data)
docker compose down -v| METHOD | ENDPOINT | DESCRIPTION |
|---|---|---|
| POST | /register |
Create user account |
| POST | /login |
Returns JWT |
| POST | /refresh |
Returns fresh JWT |
| POST | /revoke |
Revoke a refresh token |
| PATCH | /user/{id}/password |
Change user password (admin/own password only) |
| PATCH | /user/{id}/role |
Change user role (admin only) |
| GET | /users |
List all current users (admin only) |
| GET | /items |
List all items |
| GET | /items/search |
Search for items by name or sku (ex. GET /items/search?q=glove) |
| POST | /items |
Create item (manager only) |
| GET | /items/{id} |
Get single item |
| PATCH | /items/{id} |
Update item (manager only) |
| DELETE | /items/{id} |
Delete item (manager only) |
| GET | /locations |
List all locations |
| POST | /locations |
Create location (manager only) |
| GET | /locations/{id} |
Get a single location |
| PATCH | /locations/{id} |
Update location (manager only) |
| DELETE | /locations/{id} |
Delete location (manager only) |
| GET | /stock |
View all current stock levels |
| GET | /stock/alerts |
View low-stock items - quantity < low stock threshold |
| GET | /stock/item/{id} |
Stock levels for one item across locations |
| GET | /stock/location/{id} |
All items from one location |
| POST | /stock/move |
Move quantity from one location to another |
| POST | /stock/receive |
Add incoming stock to a location (manager only) |
| GET | /movements |
Item movement history (manager only) |
| GET | /movements/item/{id} |
Movement history for a single item |
# Register
curl -X POST http://localhost:8080/register \
-H "Content-Type: application/json" \
-d '{"username": "john", "password": "password123"}'
# Login - copy the token from the response
curl -X POST http://localhost:8080/login \
-H "Content-Type: application/json" \
-d '{"username": "john", "password": "password123"}'
# Use the token
curl http://localhost:8080/items \
-H "Authorization: Bearer <your-token>"-
Setup (manager): A manager logs in and creates the items and locations first. Items are things like "Heavy Duty Work Gloves" with SKU
GLOVE-HD-LG. Locations are physical spots in the warehouse, like "Aisle 3 Bay 1" or "Back Stockroom Shelf 2". Neither has any quantity yet. This is just building the catalog and map of the warehouse. -
Receiving a shipment (manager): A truck arrives with 50 pairs of gloves. The managers hits
POST /stock/receivewith the item ID, location ID, and quantity of 50. Now the warehouse actually has stock. This is the only way stock enters the system. -
Day-to-day operations (worker): A worker needs 10 pairs of gloves from Aisle 3 to fulfill an order. They move them to the "Dispatch Bay" location with
POST /stock/move. The system decrements Aisle 3 and increments Dispatch Bay. Nobody creates or destroys stock - it just moves around between locations. -
Checking stock (anyone): Anyone can hit
GET /stock/item/{id}to see where all the gloves are and how many, orGET /stock/location/{id}to see everything currently sitting in Aisle 3.
warehouse-api/
├── main.go # Server entry point
├── server.go # Server struct and helpers
├── handlers.go # HTTP handlers
├── helpers.go # Helper functions
├── middleware.go # requireAuth, requireRole
├── types.go # Custom type definitions
├── internal/
│ └── auth/ # user authentication stuff
│ └── database/ # sqlc generated code
├── sql/
│ ├── queries/ # SQL queries
│ └── schema/ # goose migrations
├── Dockerfile
├── docker-compose.yml
└── sqlc.yaml
- Low stock alerts - flag items that fall below a configurable threshold
- Audit log - append-only record of every stock movement (who did it, when, how much)
- Soft deletes - instead of hard deleting items or locations, mark them inactive
- Search and filtering on item listings (by name, SKU, low stock status)
- Receiving endpoint - bulk-add stock from a shipment to a location
- Admin role - can manage users, reset passwords, assign roles
If you'd like to contribute, please fork the repo and open a pull request to the main branch