A production‑ready e‑commerce REST API built with NestJS, TypeORM, and PostgreSQL. Includes authentication, product/catalog management, orders and Stripe payments, file uploads to AWS S3, and transactional emails.
- Authentication and authorization with JWT (access + email verification + password reset)
- Users, addresses and profile management
- Brands, categories, and products with slug utility
- Orders and Stripe payments with secure webhook handling
- File uploads to AWS S3 (Multer + AWS SDK)
- Transactional emails via Nodemailer/Mailer (Handlebars templates)
- PostgreSQL with TypeORM (sync in development)
- Validation, global pipes, and Swagger API docs
- Dockerfile and docker‑compose for local database
- NestJS 11, TypeScript 5
- TypeORM 0.3, PostgreSQL 16
- JWT (passport‑jwt), bcrypt
- Stripe 18
- AWS SDK v3 (S3), multer‑s3
- Nodemailer, @nestjs-modules/mailer, Handlebars
- Swagger (OpenAPI)
This backend pairs with a React frontend.
- Frontend repo: https://github.com/abdulbasit0-UI/React-ecommerce-frontend
- Node.js 20+
- npm 10+
- PostgreSQL 13+ (or use docker‑compose)
npm install
This spins up a local Postgres at port 5432 with default credentials.
docker compose up -d
The compose file provisions:
- DB:
ecommerce_dev
, user:postgres
, password:postgres
.
Create a .env
in the project root. Use this template and adjust values:
# App
NODE_ENV=development
PORT=3000
FRONTEND_URL=http://localhost:3001
# Database
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USERNAME=postgres
DATABASE_PASSWORD=postgres
DATABASE_NAME=ecommerce_dev
# JWTs
JWT_SECRET=replace-with-strong-secret
JWT_EXPIRES_IN=1d
JWT_EMAIL_SECRET=replace-with-email-secret
JWT_EMAIL_EXPIRES_IN=1h
JWT_RESET_SECRET=replace-with-reset-secret
JWT_RESET_EXPIRES_IN=30m
# Mail (SMTP)
MAIL_HOST=smtp.example.com
MAIL_PORT=465
MAIL_USER=[email protected]
MAIL_PASS=your-smtp-password
MAIL_FROM=[email protected]
# AWS S3
AWS_REGION=us-east-1
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret
AWS_S3_BUCKET=your-bucket-name
AWS_S3_URL=https://your-bucket.s3.amazonaws.com
# Stripe
STRIPE_SECRET_KEY=sk_test_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
Notes:
- TypeORM synchronize is enabled in development only (see
ormconfig.ts
). Do not use in production.
- Development (watch):
npm run start:dev
- Production build and start:
npm run build
npm run start:prod
The server logs:
- App:
http://localhost:<PORT>
- Swagger UI:
http://localhost:<PORT>/api-docs
Swagger is configured in src/main.ts
with Bearer auth. Once the server is running, open:
http://localhost:3000/api-docs
The webhook controller is mounted at POST /webhook/stripe
and requires raw body for signature verification.
- Ensure
STRIPE_SECRET_KEY
andSTRIPE_WEBHOOK_SECRET
are set. - When running locally, forward events to your server, for example:
stripe login
stripe listen --forward-to localhost:3000/webhook/stripe
- The app uses
rawBody: true
on bootstrap to verify the Stripe signature. If you proxy the app, preserve the raw body. - Events handled include
checkout.session.completed
andpayment_intent.succeeded
and mark orders as paid viaOrdersService
.
- S3 client is configured via
src/config/aws.config.ts
and injected ass3Client
. - Required envs:
AWS_REGION
,AWS_ACCESS_KEY_ID
,AWS_SECRET_ACCESS_KEY
, and your bucket details (AWS_S3_BUCKET
,AWS_S3_URL
). - Multer and
multer-s3
are used for streaming uploads.
- Mailer config:
src/config/mail.config.ts
. - Templating uses Handlebars. Templates live under
templates/
(e.g.,verify-email.hbs
,reset-password.hbs
). - Set SMTP via
MAIL_HOST
,MAIL_PORT
,MAIL_USER
,MAIL_PASS
, and optionallyMAIL_FROM
.
Paste this into any Mermaid‑compatible tool (e.g., https://mermaid.live
) to render the ERD:
erDiagram
USERS {
uuid id PK
string name
string email UK
string password
enum role
string phone
string avatar
date dateOfBirth
text bio
json preferences
boolean isActive
timestamptz createdAt
timestamptz updatedAt
}
ADDRESSES {
uuid id PK
enum type
string firstName
string lastName
string company
string address
string address2
string city
string state
string zipCode
string country
boolean isDefault
uuid userId FK
timestamptz createdAt
timestamptz updatedAt
}
CATEGORIES {
uuid id PK
string name UK
string slug UK
string description
boolean isActive
timestamptz createdAt
timestamptz updatedAt
}
BRANDS {
uuid id PK
string name UK
string slug UK
string description
string logo
boolean isActive
timestamptz createdAt
timestamptz updatedAt
}
PRODUCTS {
uuid id PK
string name
string slug UK
text description
decimal price
int stock
json images
uuid category_id FK
uuid brand_id FK
boolean isActive
timestamptz created_at
timestamptz updated_at
decimal rating
int reviewCount
}
ORDERS {
uuid id PK
uuid userId FK
decimal total
enum status
string stripeSessionId
string stripePaymentIntentId
timestamptz paidAt
string shippingFirstName
string shippingLastName
string shippingEmail
string shippingPhone
string shippingAddress
string shippingCity
string shippingState
string shippingZipCode
string shippingCountry
timestamptz createdAt
timestamptz updatedAt
}
ORDER_ITEMS {
uuid id PK
uuid productId FK
int quantity
decimal price
}
USERS ||--o{ ADDRESSES : has
USERS ||--o{ ORDERS : places
CATEGORIES ||--o{ PRODUCTS : contains
BRANDS ||--o{ PRODUCTS : makes
PRODUCTS ||--o{ ORDER_ITEMS : included_in
ORDERS ||--o{ ORDER_ITEMS : contains
src/
app.module.ts
main.ts
entities/ # TypeORM entities (User, Product, Order, ...)
dtos/ # DTOs for validation/typing
modules/
auth/ # Auth controller/service, JWT strategies
users/
products/
categories/
brands/
orders/
payments/ # Stripe webhook controller
mail/
aws/
common/ # guards, pipes, decorators, filters
config/ # aws and mail configuration
utils/ # slug utility
npm run start # start app
npm run start:dev # start in watch mode
npm run start:prod # start compiled build
npm run build # compile TypeScript
npm run lint # eslint fix
npm run format # prettier write
npm run test # unit tests (jest)
npm run test:e2e # e2e tests
A Dockerfile
builds the app image. To build and run:
docker build -t ecommerce-backend .
docker run -p 3000:3000 --env-file .env ecommerce-backend
A docker-compose.yml
is provided for PostgreSQL. Start the DB first or point DATABASE_*
to your own instance.
- Set
NODE_ENV=production
and disable TypeORM synchronization (already off unless development). - Use managed Postgres and S3 credentials with least privilege.
- Ensure
JWT_*
secrets are strong and rotated periodically. - Configure HTTPS and a reverse proxy (e.g., Nginx) that preserves raw body for Stripe webhook.
MIT. See LICENSE.