Bookstore REST API
Build a REST API in Go for a small online bookstore. It manages the book catalog with stock levels and customer orders: clients can list and search books, add and update them, and place an order for several books at once. Placing an order checks and reduces the stock inside a database transaction, so two customers can never buy the last copy. You'll use the Gin web framework, PostgreSQL with the sqlx library, SQL migrations, request validation, pagination, and middleware for logging and error handling.
- Estimate
- ~13.5h
- Steps
- 7
- Completed by
- 0
- Proposed by
- codeseed.app
Gin · PostgreSQL · sqlx
Project roadmap
- 01
Set up the project
~1.5hRun go mod init and add github.com/gin-gonic/gin, github.com/jmoiron/sqlx and the PostgreSQL driver github.com/jackc/pgx/v5/stdlib. Read the database URL and port from environment variables. Start a Gin router with a GET /health route that pings the database and returns 200 if it's reachable, 503 if not. Run PostgreSQL locally, for example with Docker. Done when: /health returns 200 with the database running and 503 when it's stopped.
- 02
Create the schema
~1.5hWrite SQL migrations (with golang-migrate, or plain SQL files run at startup) for books (id, unique isbn, title, author, price in cents, stock that can't go below 0), orders (id, customer_email, status, total_cents, created_at) and order_items (order_id, book_id, quantity, unit_price_cents), with foreign keys and a few sample books. Done when: the migrations create the tables and sample data on an empty database.
- 03
Book endpoints
~2.5hImplement GET /books, GET /books/:id, POST /books, PUT /books/:id and DELETE /books/:id. Bind JSON into structs with Gin's binding tags (title and author required, price above 0, stock 0 or more) and use sqlx for every query. Return 400 for invalid input, 404 for unknown ids and 409 for a duplicate ISBN. Done when: all five endpoints work with curl and return these status codes.
- 04
Pagination and filtering
~1.5hAdd ?page=, ?limit= (default 20, maximum 100), ?author= and ?q= (case-insensitive title search) to GET /books. Build the query with parameters, never by concatenating strings. Return the books together with total, page and limit fields. Done when: page 2 with a limit of 5 returns the right books and the correct total.
- 05
Place orders in a transaction
~3hImplement POST /orders with a customer email and a list of book ids and quantities. In one sqlx transaction, lock each book row with SELECT ... FOR UPDATE, check its stock, reduce it, and insert the order and its items at the current prices. If any book doesn't have enough stock, roll back everything and return 409 naming that book. Add GET /orders/:id with the order's items. Done when: an order larger than the stock is refused and no stock changes.
- 06
Add middleware
~1.5hAdd a logging middleware that records the method, path, status and duration of every request, and an error-handling middleware so that every failure becomes the same JSON shape, like {"error":"..."}. Unexpected errors return 500 without internal details. Done when: every request is logged and every error response has the same JSON structure.
- 07
Write tests
~2hWrite tests with net/http/httptest against a test database: create a book, order it successfully, try to order more than the remaining stock, and check the stock afterwards. Send two orders for the last copy at the same time and verify that only one succeeds. Done when: go test ./... passes.
Resources
Ready to build this?
Get a GitHub repo and start building. Your AI reviewer checks each step as you go.
Tech stack