Production-ready e-commerce REST API built with NestJS, TypeScript, TypeORM, and PostgreSQL.
This project implements a modular backend for a typical online store, including authentication, users, categories, products, inventory, cart, wishlist, orders, addresses, reviews, coupons, shipping, and admin analytics.
- Framework: NestJS 11
- Language: TypeScript with strict mode
- ORM: TypeORM
- Database: PostgreSQL
- Auth: JWT access token + refresh token
- Validation:
class-validator+class-transformer - Config:
@nestjs/config - Password hashing:
bcrypt - API docs:
@nestjs/swagger
- Modular NestJS architecture
- Global request validation
- Global exception filter
- Global response envelope
- JWT authentication
- Role-based authorization for admin routes
- Swagger documentation
- Product catalog with variants and images
- Inventory management
- Cart and wishlist flows
- Order creation and cancellation
- Address management
- Product reviews with purchase validation
- Coupon validation and admin coupon management
- Shipping estimation and tracking
- Admin dashboard statistics
src/
├── main.ts
├── app.module.ts
├── app.controller.ts
├── app.service.ts
├── common/
│ ├── decorators/
│ ├── enums/
│ ├── filters/
│ ├── guards/
│ ├── interceptors/
│ ├── interfaces/
│ ├── pipes/
│ └── utils/
├── config/
│ └── database.config.ts
└── modules/
├── admin/
├── addresses/
├── auth/
├── cart/
├── categories/
├── coupons/
├── inventory/
├── orders/
├── products/
├── reviews/
├── shipping/
├── users/
└── wishlist/
Base prefix:
/api/v1
Swagger UI:
/api/docs
Success response shape:
{
"success": true,
"data": {},
"message": "Request completed successfully"
}Error response shape:
{
"success": false,
"statusCode": 400,
"message": "Validation failed",
"errors": [],
"path": "/api/v1/example",
"timestamp": "2026-04-11T00:00:00.000Z"
}Validation behavior:
whitelist: trueforbidNonWhitelisted: truetransform: true
- Access token is used in the
Authorization: Bearer <token>header - Refresh token is stored hashed on the user record
- Email verification is required before login
Supported roles:
ADMINCUSTOMER
Admin endpoints use:
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles(Role.ADMIN)For fully admin-only controllers such as inventory and admin, guards are applied at class level.
Use .env.example as the template.
PORT=3000
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USER=postgres
DATABASE_PASSWORD=secret
DATABASE_NAME=ecommerce_db
DATABASE_SYNCHRONIZE=false
DATABASE_LOGGING=false
JWT_ACCESS_SECRET=access_secret
JWT_REFRESH_SECRET=refresh_secret
JWT_ACCESS_EXPIRY=15m
JWT_REFRESH_EXPIRY=7d
BCRYPT_ROUNDS=10Notes:
DATABASE_SYNCHRONIZE=trueis useful for local development against an empty database- keep
DATABASE_SYNCHRONIZE=falsefor production - if port
3000is already in use locally, setPORT=3001
- Install dependencies:
npm install-
Create
.envfrom.env.example -
Start PostgreSQL and make sure the configured database exists
-
Run the API:
npm run start:dev- Open Swagger:
http://localhost:3000/api/docs
If you changed PORT, adjust the Swagger URL accordingly.
npm run build- compile the projectnpm run start- start the appnpm run start:dev- start in watch modenpm run start:prod- run compiled buildnpm test- run unit testsnpm run test:e2e- run e2e smoke testsnpm run test:cov- run coveragenpm run lint- run eslint
The app is configured for PostgreSQL via TypeORM with autoLoadEntities: true.
Important local notes:
- this project expects a reachable PostgreSQL server
- if you reuse an existing shared database, schema sync can collide with unrelated tables
- prefer using a dedicated database such as
ecommerce_dbfor this project
- register user
- verify email
- login and issue tokens
- refresh token rotation
- forgot/reset password
- logout
- categories
- products
- variants
- product images
- featured and search endpoints
- cart
- wishlist
- orders
- coupons
- shipping estimates
- profile management
- addresses
- reviews
- user management
- inventory management
- coupon management
- order administration
- revenue and dashboard analytics
Access labels:
Public= no token requiredJWT= authenticated user requiredAdmin= authenticatedADMINuser required
| Method | Path | Access | Description |
|---|---|---|---|
| GET | /api/v1/health |
Public | Health check |
| Method | Path | Access | Description |
|---|---|---|---|
| POST | /api/v1/auth/register |
Public | Register a customer |
| POST | /api/v1/auth/login |
Public | Login and receive access/refresh tokens |
| POST | /api/v1/auth/logout |
JWT | Logout current user |
| POST | /api/v1/auth/refresh-token |
Public | Refresh token pair |
| POST | /api/v1/auth/forgot-password |
Public | Start password reset flow |
| POST | /api/v1/auth/reset-password |
Public | Reset password using token |
| POST | /api/v1/auth/verify-email |
Public | Verify email address |
| Method | Path | Access | Description |
|---|---|---|---|
| GET | /api/v1/users/me |
JWT | Get current user profile |
| PUT | /api/v1/users/me |
JWT | Update current user profile |
| DELETE | /api/v1/users/me |
JWT | Delete current user account |
| GET | /api/v1/users |
Admin | Get all users |
| GET | /api/v1/users/:id |
Admin | Get user by id |
| PATCH | /api/v1/users/:id/role |
Admin | Update user role |
| DELETE | /api/v1/users/:id |
Admin | Delete user by id |
| Method | Path | Access | Description |
|---|---|---|---|
| GET | /api/v1/categories |
Public | Get all categories |
| GET | /api/v1/categories/:id |
Public | Get category by id |
| GET | /api/v1/categories/:id/products |
Public | Get products in category |
| POST | /api/v1/categories |
Admin | Create category |
| PUT | /api/v1/categories/:id |
Admin | Update category |
| DELETE | /api/v1/categories/:id |
Admin | Delete category |
| Method | Path | Access | Description |
|---|---|---|---|
| GET | /api/v1/products |
Public | Get paginated products |
| GET | /api/v1/products/featured |
Public | Get featured products |
| GET | /api/v1/products/new-arrivals |
Public | Get new arrivals |
| GET | /api/v1/products/search?q= |
Public | Search products |
| GET | /api/v1/products/slug/:slug |
Public | Get product by slug |
| GET | /api/v1/products/:id |
Public | Get product by id |
| GET | /api/v1/products/:id/variants |
Public | Get product variants |
| POST | /api/v1/products |
Admin | Create product |
| PUT | /api/v1/products/:id |
Admin | Update product |
| DELETE | /api/v1/products/:id |
Admin | Delete product |
| POST | /api/v1/products/:id/variants |
Admin | Create variant |
| PUT | /api/v1/products/:id/variants/:variantId |
Admin | Update variant |
| DELETE | /api/v1/products/:id/variants/:variantId |
Admin | Delete variant |
| POST | /api/v1/products/:id/images |
Admin | Add product image |
| DELETE | /api/v1/products/:id/images/:imageId |
Admin | Delete product image |
| Method | Path | Access | Description |
|---|---|---|---|
| GET | /api/v1/inventory |
Admin | Get all inventory records |
| GET | /api/v1/inventory/low-stock |
Admin | Get low stock products |
| GET | /api/v1/inventory/out-of-stock |
Admin | Get out of stock products |
| GET | /api/v1/inventory/:productId |
Admin | Get product inventory |
| GET | /api/v1/inventory/:productId/variants/:variantId |
Admin | Get variant inventory |
| PATCH | /api/v1/inventory/:productId/adjust |
Admin | Adjust product stock |
| PATCH | /api/v1/inventory/:productId/variants/:variantId/adjust |
Admin | Adjust variant stock |
| Method | Path | Access | Description |
|---|---|---|---|
| GET | /api/v1/cart |
JWT | Get current cart |
| POST | /api/v1/cart/items |
JWT | Add item to cart |
| PUT | /api/v1/cart/items/:itemId |
JWT | Update cart item quantity |
| DELETE | /api/v1/cart/items/:itemId |
JWT | Remove cart item |
| DELETE | /api/v1/cart |
JWT | Clear cart |
| Method | Path | Access | Description |
|---|---|---|---|
| GET | /api/v1/wishlist |
JWT | Get current wishlist |
| POST | /api/v1/wishlist/items |
JWT | Add product to wishlist |
| DELETE | /api/v1/wishlist/items/:productId |
JWT | Remove product from wishlist |
| Method | Path | Access | Description |
|---|---|---|---|
| POST | /api/v1/orders |
JWT | Create order from cart |
| GET | /api/v1/orders |
JWT | Get current user orders |
| GET | /api/v1/orders/:id |
JWT | Get current user order by id |
| POST | /api/v1/orders/:id/cancel |
JWT | Cancel current user order |
| GET | /api/v1/orders/:id/invoice |
JWT | Get invoice for order |
| GET | /api/v1/orders/admin/all |
Admin | Get all orders |
| PATCH | /api/v1/orders/:id/status |
Admin | Update order status |
| GET | /api/v1/orders/admin/stats |
Admin | Get order statistics |
| Method | Path | Access | Description |
|---|---|---|---|
| GET | /api/v1/addresses |
JWT | Get current user addresses |
| POST | /api/v1/addresses |
JWT | Create address |
| PUT | /api/v1/addresses/:id |
JWT | Update address |
| DELETE | /api/v1/addresses/:id |
JWT | Delete address |
| PATCH | /api/v1/addresses/:id/default |
JWT | Set default address |
| Method | Path | Access | Description |
|---|---|---|---|
| GET | /api/v1/products/:productId/reviews |
Public | Get product reviews |
| POST | /api/v1/products/:productId/reviews |
JWT | Create review |
| PUT | /api/v1/products/:productId/reviews/:reviewId |
JWT | Update own review |
| DELETE | /api/v1/products/:productId/reviews/:reviewId |
JWT | Delete own review |
| Method | Path | Access | Description |
|---|---|---|---|
| POST | /api/v1/coupons/validate |
Public | Validate coupon code |
| GET | /api/v1/coupons |
Admin | Get all coupons |
| POST | /api/v1/coupons |
Admin | Create coupon |
| PUT | /api/v1/coupons/:id |
Admin | Update coupon |
| DELETE | /api/v1/coupons/:id |
Admin | Delete coupon |
| Method | Path | Access | Description |
|---|---|---|---|
| GET | /api/v1/shipping/methods |
Public | Get active shipping methods |
| POST | /api/v1/shipping/estimate |
JWT | Estimate shipping cost |
| GET | /api/v1/shipping/track/:trackingNumber |
JWT | Get mock tracking information |
| Method | Path | Access | Description |
|---|---|---|---|
| GET | /api/v1/admin/stats/overview |
Admin | Get overview metrics |
| GET | /api/v1/admin/stats/revenue?range=daily|weekly|monthly |
Admin | Get grouped revenue data |
| GET | /api/v1/admin/stats/top-products |
Admin | Get top selling products |
| GET | /api/v1/admin/stats/recent-orders |
Admin | Get latest orders |
During local setup in this workspace:
- the app was successfully started against Dockerized PostgreSQL
- Swagger was verified at
http://localhost:3001/api/docs - local
.envcurrently uses port3001to avoid a conflict with another container already using3000
That local override is not required for every environment. Use whatever PORT fits your machine.