A production-ready e-commerce application built with Go (REST + gRPC backend) and React (web frontend).
The application runs two servers concurrently (backend) plus a React web frontend:
- HTTP (REST) — Gin framework, port
8888 - gRPC — port
8889, with JWT auth interceptor
Each domain (user, product, order, cart) follows a ports-and-adapters layout:
internal/{domain}/
├── model/ # GORM models
├── dto/ # Request/response structs with validation tags
├── repository/ # Database access (depends on dbs.Database interface)
├── service/ # Business logic (depends on repository interfaces)
└── port/
├── http/ # Gin handlers and route registration
└── grpc/ # gRPC handlers and server registration
| Domain | HTTP | gRPC |
|---|---|---|
| user | ✓ | ✓ |
| product | ✓ | ✓ |
| order | ✓ | ✓ |
| cart | ✓ | ✓ |
Backend
| Concern | Library |
|---|---|
| HTTP framework | Gin v1.12 |
| gRPC | grpc-go v1.79 |
| ORM | GORM v1.31 + PostgreSQL |
| Cache | go-redis v9 |
| Auth | JWT (golang-jwt v5) |
| Validation | gocommon/validation |
| API Docs | Swagger |
| Testing | testify v1.11 + mockery |
| Proto codegen | buf + protobuf v1.36 |
Frontend
| Concern | Library |
|---|---|
| Framework | React 18 + TypeScript |
| Build tool | Vite |
| Styling | Tailwind CSS |
| Routing | React Router v6 |
| Data fetching | TanStack Query |
| Forms | React Hook Form + Zod |
| HTTP client | Axios |
- Go 1.26+
- Node.js 18+
- PostgreSQL
- Redis
Docker Compose for local dependencies: docker-compose-template
1. Clone and configure
git clone https://github.com/quangdangfit/goshop.git
cd goshop
cp pkg/config/config.sample.yaml pkg/config/config.yamlEdit pkg/config/config.yaml:
environment: production
http_port: 8888
grpc_port: 8889
auth_secret: your-secret-key
database_uri: postgres://username:password@localhost:5432/goshop
redis_uri: localhost:6379
redis_password:
redis_db: 02. Run the backend
go run cmd/api/main.goINFO HTTP server is listening on PORT: 8888
INFO GRPC server is listening on PORT: 8889
3. Run the web frontend
cd web
npm install
npm run devWeb UI: http://localhost:3000
The frontend proxies all
/apirequests to the backend athttp://localhost:8888, so both servers must be running.
4. Browse the API
Swagger UI: http://localhost:8888/swagger/index.html
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/auth/register |
Register |
| POST | /api/v1/auth/login |
Login |
| POST | /api/v1/auth/refresh |
Refresh access token |
| GET | /api/v1/auth/me |
Get current user |
| PUT | /api/v1/auth/change-password |
Change password |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/addresses |
List my addresses |
| POST | /api/v1/addresses |
Create address |
| GET | /api/v1/addresses/:id |
Get address |
| PUT | /api/v1/addresses/:id |
Update address |
| DELETE | /api/v1/addresses/:id |
Delete address |
| PUT | /api/v1/addresses/:id/default |
Set default address |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/wishlist |
Get my wishlist |
| POST | /api/v1/wishlist |
Add product to wishlist |
| DELETE | /api/v1/wishlist/:productId |
Remove from wishlist |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/categories |
List categories |
| GET | /api/v1/categories/:id |
Get category |
| POST | /api/v1/categories |
Create category (auth) |
| PUT | /api/v1/categories/:id |
Update category (auth) |
| DELETE | /api/v1/categories/:id |
Delete category (auth) |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/products |
List products (cached) |
| GET | /api/v1/products/:id |
Get product (cached) |
| POST | /api/v1/products |
Create product (auth) |
| PUT | /api/v1/products/:id |
Update product (auth) |
| GET | /api/v1/products/:id/reviews |
List product reviews |
| POST | /api/v1/products/:id/reviews |
Create review (auth) |
| PUT | /api/v1/products/:id/reviews/:reviewId |
Update review (auth) |
| DELETE | /api/v1/products/:id/reviews/:reviewId |
Delete review (auth) |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/orders |
Place order |
| GET | /api/v1/orders |
List my orders |
| GET | /api/v1/orders/:id |
Get order details |
| PUT | /api/v1/orders/:id/cancel |
Cancel order |
| PUT | /api/v1/orders/:id/status |
Update order status (admin) |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/coupons |
Create coupon (auth) |
| GET | /api/v1/coupons/:code |
Get coupon by code |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/cart |
Get my cart |
| POST | /api/v1/cart |
Add / update product in cart |
| DELETE | /api/v1/cart/:productId |
Remove product from cart |
Run all tests with coverage
make unittestRun a single test suite
go test ./internal/product/service/... -v -run TestProductServiceTestSuiteRun a single test case
go test ./internal/product/service/... -v -run TestProductServiceTestSuite/TestCreateSuccessRegenerate mocks
make mockRegenerate Swagger docs
make docRegenerate proto (Uses https://buf.build)
cd proto && make build