Freelance Tech Stack - Tài liệu tham khảo
Tổng hợp từ góc nhìn thực tế cho solo developer / freelancer.
Stack nền tảng: Strapi + NestJS + Refine + Next.js
Deploy: AWS (BE/CMS) + Vercel (Next.js) + Cloudflare Pages (Refine)
Tổng quan kiến trúc
Cloudflare DNS
│
├── example.com → Vercel (Next.js — End User UI)
├── admin.example.com → CF Pages (Refine — Admin Panel)
├── api.example.com → AWS App Runner (NestJS — Business Logic)
└── cms.example.com → AWS EC2 (Strapi — CMS)
│
AWS RDS PostgreSQL
│
AWS S3 + CloudFront
(Media)Layer Map
┌────────────────────────────────────────────────────┐
│ END USER │
│ Next.js (Vercel) │
└────────────────────────┬───────────────────────────┘
│ HTTP / GraphQL
┌────────────────────────▼───────────────────────────┐
│ BUSINESS LOGIC │
│ NestJS (AWS App Runner) │
│ Auth │ Payment │ Workflow │ Events │
└──────┬────────────────────────────────────┬────────┘
│ │
┌──────▼──────┐ ┌────────▼────────┐
│ Strapi │ │ Refine │
│ (AWS EC2) │ │ (CF Pages) │
│ Content │ │ Operations │
│ Media→S3 │ │ Analytics │
└──────┬──────┘ └────────┬────────┘
│ │
└─────────────────┬──────────────────┘
│
┌───────▼───────┐
│ PostgreSQL │
│ (AWS RDS) │
└───────────────┘1. NestJS — Business Logic Layer
Là gì?
Backend framework TypeScript-first, chạy trên Node.js. Đóng vai trò trung tâm xử lý logic của toàn hệ thống.
Nên dùng khi
- Cần viết business logic phức tạp (tính toán, workflow, rule...)
- Cần tích hợp third-party (Stripe, email, SMS, OAuth...)
- Cần auth riêng (JWT, refresh token, RBAC)
- Cần expose REST API hoặc GraphQL cho frontend
- Cần background jobs (queue, cron, event-driven)
Không nên dùng khi
- Project chỉ là landing page tĩnh — Next.js API routes đủ
- Chỉ cần CRUD đơn giản, không có logic — Strapi tự xử lý được
Ví dụ thực tế
✅ Cần NestJS — E-commerce platform:
POST /orders → validate cart, check stock, tính giá
POST /orders/:id/pay → tích hợp Stripe, xử lý payment
POST /users/verify → gửi OTP, verify phone number
GET /reports/revenue → aggregate data, trả về chart data
❌ Không cần NestJS — Blog cá nhân:
- Chỉ cần Strapi + Next.js
- Strapi expose API, Next.js fetch và render
- Không có business logic phức tạp
→ Thêm NestJS vào là over-engineer2. Strapi — Content Management Layer
Là gì?
Headless CMS open-source. Đóng vai trò quản lý content và media, expose REST/GraphQL API. Non-technical editor tự quản lý mà không cần dev.
Nên dùng khi
- Client cần tự quản lý content mà không cần dev can thiệp
- Schema content thay đổi thường xuyên (thêm field, thêm collection)
- Cần media library (upload ảnh, video, file)
- Non-technical editor cần GUI để làm việc
- Cần draft/publish workflow
Không nên dùng khi
- Data là transactional (orders, payments) — để NestJS + PostgreSQL xử lý
- Cần logic phức tạp trên data — Strapi plugin hạn chế
- Team toàn developer, không có non-technical editor — Payload CMS tốt hơn
- Cần real-time data
Ví dụ thực tế
✅ Nên dùng Strapi:
- Website công ty: bài viết blog, news, team members
- E-commerce: product catalog, categories, banners
- App du lịch: địa điểm, review, hình ảnh
- Marketing site: landing page content, testimonials
- Mobile app: banners, notifications, remote config
❌ Không nên dùng Strapi cho:
- Quản lý đơn hàng (orders) → NestJS + DB
- Quản lý giao dịch tài chính → NestJS + DB
- Real-time chat messages → WebSocket + Redis
- User activity logs → NestJS + DBStrapi vs Payload CMS
| Tiêu chí | Strapi | Payload |
|---|---|---|
| Schema tạo bằng | GUI kéo thả | TypeScript code |
| Phù hợp với | Non-tech editor | Developer team |
| SQLite dev experience | ✅ Tốt | ❌ Rough |
| Feature gate | Một số feature Enterprise | Không, tất cả free |
| GraphQL | Add-on | Add-on |
| Recommend cho | Freelance (client tự dùng) | Internal team toàn dev |
3. Refine — Admin Panel Layer
Là gì?
React meta-framework để build admin panel / internal tools. Không có database — chỉ là UI layer connect vào API (NestJS hoặc Strapi). Khác với Strapi, Refine dùng cho thao tác nghiệp vụ, không chỉ quản lý content.
Nên dùng khi
- Cần admin panel với business actions (verify, approve, ban, refund...)
- Cần custom workflow phức tạp hơn CRUD
- Cần analytics / dashboard tự build
- Internal tool cho operation team
- Client cần xem report + thao tác trên data
Không nên dùng khi
- Client chỉ cần quản lý content đơn giản — Strapi admin panel đủ
- Chỉ cần xem report/chart thuần túy — Metabase nhanh hơn
- Team không biết React — học curve cao
Ví dụ thực tế
✅ Nên dùng Refine:
- Order management: xem đơn hàng, approve, cancel, refund
- User management: xem users, verify KYC, ban account
- Content moderation: review bài đăng, approve/reject
- Support dashboard: xem tickets, assign, resolve
- Finance dashboard: xem transactions, export report
❌ Không nên dùng Refine khi:
- Client chỉ cần viết blog → Strapi admin đủ
- Chỉ cần xem chart doanh thu → Metabase đơn giản hơn
- Project quá nhỏ, 1-2 màn CRUD → tự build nhanh hơnRefine vs Metabase
| Refine | Metabase | |
|---|---|---|
| Là gì | Admin panel framework (code) | BI / Analytics tool (no-code) |
| Business actions | ✅ Có (verify, approve...) | ❌ Không, read-only |
| Setup | Cần code React | Connect DB là xong |
| Customization | Không giới hạn | Hạn chế |
| Phù hợp | Operation team cần action | Stakeholder xem report |
| Khi nào dùng | Client cần làm nghiệp vụ | Client chỉ cần xem số liệu |
Cách Refine hoạt động
NestJS expose API:
GET /products
POST /products
PATCH /products/:id
DELETE /products/:id
Refine tự generate:
/admin/products → List với filter, sort, pagination
/admin/products/create → Form tạo mới
/admin/products/:id/edit → Form edit
/admin/products/:id/show → Detail view
Custom action ngoài CRUD:
useCustomMutation() → gọi bất kỳ endpoint nào
Custom page → Dashboard, Analytics tự viết
useStepsForm() → Multi-step form4. Next.js — Frontend Layer
Là gì?
React framework fullstack. Đóng vai trò giao diện cho end user, deploy trên Vercel.
Nên dùng khi
- Cần SEO (SSR, SSG)
- Cần performance tốt (Image optimization, lazy loading)
- Hầu hết mọi project có end-user facing UI
- Cần fullstack nhẹ (API Routes cho logic đơn giản)
Không nên dùng khi
- Project chỉ có admin panel — Refine đủ
- Static site hoàn toàn — Astro nhẹ hơn, tốt hơn cho SEO
5. PostgreSQL — Database
Là gì?
Relational database. Primary database cho toàn hệ thống, chạy trên AWS RDS.
Nên dùng khi
- Hầu như mọi project — PostgreSQL là default tốt nhất
- Data có relation phức tạp
- Cần ACID transactions (payment, orders)
- Cần full-text search (built-in)
Mở rộng khi cần
+ Redis → cache, session, queue, rate limiting
+ S3 → file storage, media uploads
+ TimescaleDB → time-series data (metrics, logs)6. Prisma — ORM Layer
Là gì?
TypeScript-native ORM dùng trong NestJS. Thay thế TypeORM. Drizzle ORM là lựa chọn nhẹ hơn nếu cần performance cao.
Nên dùng khi
- Project mới với NestJS + PostgreSQL
- Cần type-safe database queries
- Cần migration workflow rõ ràng
// TypeORM — verbose, dễ lỗi type
const user = await userRepository.findOne({ where: { id } })
// Prisma — clean, fully typed, autocomplete tốt
const user = await prisma.user.findUnique({ where: { id } })7. Metabase — Analytics Layer (Optional)
Là gì?
BI tool self-host. Connect thẳng vào PostgreSQL, build chart/report bằng GUI. Read-only — không thao tác được trên data.
Nên dùng khi
- Client cần xem report phức tạp, nhiều chart
- Client muốn tự build report không cần dev
- Cần export PDF/Excel
- Stakeholder / manager cần dashboard riêng
Không nên dùng khi
- Client cần thao tác nghiệp vụ (verify, approve) → Refine
- Project nhỏ, vài con số đơn giản → Refine tự build được
8. Deploy Stack — Cloud Architecture
Domain & Subdomain convention
example.com → Next.js (end user)
api.example.com → NestJS (REST API)
cms.example.com → Strapi (content editor)
admin.example.com → Refine (operation team)
analytics.example.com → Metabase (stakeholder - optional)Service → Cloud mapping
| Service | Cloud | Lý do |
|---|---|---|
| Next.js | Vercel | Zero config, preview deployments, edge network |
| Refine | Cloudflare Pages | Static SPA, free tier rộng, không cần server |
| NestJS | AWS App Runner | Managed container, auto scale, không manage server |
| Strapi | AWS EC2 t3.small | Cần persistent storage, full control |
| Database | AWS RDS PostgreSQL | Managed, auto backup, production-grade |
| Media | AWS S3 + CloudFront | Strapi upload provider sẵn có |
| DNS | Cloudflare | Free, SSL tự động, proxy bảo vệ IP |
Chi phí ước tính / tháng (1 client project)
Vercel (Next.js) → Free (hobby) / $20 (pro)
Cloudflare Pages (Refine) → Free
AWS App Runner (NestJS) → ~$10-15
AWS EC2 t3.small (Strapi) → ~$15
AWS RDS t3.micro (Postgres) → ~$15
AWS S3 + CloudFront (Media) → ~$1-5
Cloudflare DNS → Free
Tổng: ~$40-55/month
→ Charge client riêng, không gộp vào project feeNestJS deploy options so sánh
Option A — EC2 (t3.micro/small)
✅ Full control, free tier 1 năm
❌ Tự manage (update, monitor, patch)
💰 ~$15-20/month
Option B — App Runner ← RECOMMEND
✅ Deploy từ Docker / GitHub
✅ Auto scale, không manage server
✅ Quen với AWS ecosystem
💰 ~$10-15/month
Option C — ECS Fargate
✅ Production-grade, flexible
❌ Setup phức tạp hơn
💰 Đắt hơn cho small projectNginx reverse proxy (nếu dùng VPS thay cloud)
# cms.example.com → Strapi :1337
server {
server_name cms.example.com;
location / { proxy_pass http://localhost:1337; }
}
# api.example.com → NestJS :3000
server {
server_name api.example.com;
location / { proxy_pass http://localhost:3000; }
}
# admin.example.com → Refine :4000
server {
server_name admin.example.com;
location / { proxy_pass http://localhost:4000; }
}Lưu ý quan trọng khi freelance
✅ Tạo AWS account riêng cho mỗi client
✅ Transfer ownership khi bàn giao project
✅ Charge infrastructure cost riêng (không gộp vào fee)
❌ Không để nhiều client chung 1 AWS account
→ Rủi ro billing, security, isolation9. Environments — Staging & Production
Triết lý
Lúc đầu chỉ cần Staging. Production thêm sau khi client confirm.
Lý do không setup Production ngay từ đầu:
- Tiết kiệm chi phí trong giai đoạn dev/review
- Tránh maintain 2 môi trường song song khi còn đang build
- Client chưa cần Production cho đến khi go-live
Hai môi trường cần có
| Staging (STG) | Production (PROD) | |
|---|---|---|
| Mục đích | Dev, test, client review | End user thực tế |
| Data | Fake / seed data | Real data |
| Downtime OK? | ✅ Có thể | ❌ Không |
| Deploy trigger | Mỗi push lên main / develop | Manual hoặc tag release |
| Chi phí | Thấp hơn (instance nhỏ hơn) | Đầy đủ, có thể multi-AZ |
| Khi nào setup | Ngay từ đầu | Trước go-live 1-2 tuần |
Convention subdomain theo environment
── Staging ──────────────────────────────────────
stg.example.com → Next.js (STG)
api.stg.example.com → NestJS (STG)
cms.stg.example.com → Strapi (STG)
admin.stg.example.com → Refine (STG)
── Production ───────────────────────────────────
example.com → Next.js (PROD)
api.example.com → NestJS (PROD)
cms.example.com → Strapi (PROD)
admin.example.com → Refine (PROD)Infrastructure theo environment
Staging — Setup tối giản, tiết kiệm chi phí
Next.js → Vercel (preview branch deploy — free)
Refine → Cloudflare Pages (preview branch — free)
NestJS → AWS App Runner (nhỏ hơn: 0.25 vCPU / 0.5GB RAM)
Strapi → AWS EC2 t3.micro (free tier nếu còn)
PostgreSQL → AWS RDS t3.micro (single-AZ, no backup bắt buộc)
S3 → Cùng bucket với PROD nhưng prefix khác: /stg/...Chi phí STG ước tính: ~$15-25/month
Production — Đầy đủ, reliable
Next.js → Vercel (production branch)
Refine → Cloudflare Pages (production branch)
NestJS → AWS App Runner (lớn hơn: 1 vCPU / 2GB RAM)
Strapi → AWS EC2 t3.small (với EBS snapshot hàng ngày)
PostgreSQL → AWS RDS t3.small (Multi-AZ, auto backup 7 ngày)
S3 → Bucket riêng, versioning enabled
CloudFront → CDN cho S3 mediaChi phí PROD ước tính: ~$50-70/month
Environment Variables — Tách biệt hoàn toàn
Mỗi environment có file .env riêng, không bao giờ share secret giữa STG và PROD.
# .env.staging
NODE_ENV=staging
DATABASE_URL=postgresql://user:pass@stg-db.rds.amazonaws.com/myapp_stg
STRIPE_SECRET_KEY=sk_test_xxxx # Stripe test key
NEXT_PUBLIC_API_URL=https://api.stg.example.com
STRAPI_URL=https://cms.stg.example.com
AWS_S3_BUCKET=myapp-stg-media
# .env.production
NODE_ENV=production
DATABASE_URL=postgresql://user:pass@prod-db.rds.amazonaws.com/myapp_prod
STRIPE_SECRET_KEY=sk_live_xxxx # Stripe live key
NEXT_PUBLIC_API_URL=https://api.example.com
STRAPI_URL=https://cms.example.com
AWS_S3_BUCKET=myapp-prod-mediaLưu trữ secrets:
AWS Secrets Manager → secrets của NestJS, Strapi (PROD)
Vercel Env Vars → secrets của Next.js
CF Pages Env Vars → secrets của Refine
GitHub Secrets → secrets dùng trong CI/CDGit branching strategy phù hợp
main ──────────────────────────────────────► PRODUCTION
│
└── develop ───────────────────────────► STAGING
│
├── feature/login
├── feature/payment
└── fix/order-bugWorkflow:
1. Developer tạo feature branch từ develop
2. PR vào develop → auto deploy lên STG
3. Client review trên stg.example.com
4. Khi đủ features → merge develop vào main
5. main → auto deploy lên PRODCI/CD với GitHub Actions
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches:
- develop # → Staging
- main # → Production
jobs:
deploy-nestjs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set environment
run: |
if [ "${{ github.ref }}" == "refs/heads/main" ]; then
echo "ENV=production" >> $GITHUB_ENV
echo "APP_RUNNER_ARN=${{ secrets.PROD_APP_RUNNER_ARN }}" >> $GITHUB_ENV
else
echo "ENV=staging" >> $GITHUB_ENV
echo "APP_RUNNER_ARN=${{ secrets.STG_APP_RUNNER_ARN }}" >> $GITHUB_ENV
fi
- name: Build & push Docker image
run: |
docker build -t nestjs-app .
# push to ECR...
- name: Deploy to App Runner
run: |
aws apprunner start-deployment \
--service-arn $APP_RUNNER_ARNVercel và Cloudflare Pages tự động detect branch — không cần config CI/CD thêm.
Checklist trước khi go-live (STG → PROD)
Infrastructure:
☐ Tạo RDS PROD (Multi-AZ, backup enabled)
☐ Tạo EC2 PROD cho Strapi (với snapshot policy)
☐ Tạo App Runner PROD cho NestJS
☐ Setup S3 PROD bucket (versioning on)
☐ Setup CloudFront distribution
☐ Config subdomain PROD trên Cloudflare DNS
☐ SSL certificate tự động qua Cloudflare
Environment:
☐ Tạo secrets PROD trên AWS Secrets Manager
☐ Set env vars PROD trên Vercel
☐ Set env vars PROD trên Cloudflare Pages
☐ Đổi Stripe key từ test → live
☐ Kiểm tra tất cả NEXT_PUBLIC_* URLs trỏ đúng PROD
Data:
☐ Seed data cần thiết cho PROD (categories, config...)
☐ Tạo admin account PROD (khác STG)
☐ KHÔNG copy data từ STG sang PROD
Testing:
☐ Smoke test toàn bộ flow trên PROD trước khi announce
☐ Test payment với Stripe live (small amount)
☐ Test email/SMS gửi đúng
☐ Kiểm tra SSL hoạt độngLộ trình setup thực tế
Tuần 1-2 (bắt đầu project):
✅ Setup STG hoàn chỉnh
✅ CI/CD tự động deploy lên STG
✅ Client bắt đầu review trên stg.example.com
Tuần N-1 (trước go-live 1-2 tuần):
✅ Setup PROD infrastructure
✅ Test deployment lên PROD
✅ Smoke test toàn bộ
Ngày go-live:
✅ Deploy lên PROD
✅ DNS cutover (trỏ domain về PROD)
✅ Monitor 24h đầuDecision Framework — Chọn tool như thế nào?
Client cần gì?
│
├── Quản lý content (bài viết, sản phẩm, media)?
│ └── → Strapi
│
├── Business logic phức tạp (payment, auth, workflow)?
│ └── → NestJS
│
├── Admin panel với nghiệp vụ (verify, approve, ban)?
│ └── → Refine
│
├── Xem report / chart?
│ ├── Đơn giản, vài cái, có action → Refine tự build
│ └── Phức tạp, read-only, client tự dùng → Metabase
│
├── Frontend cho end user, cần SEO?
│ └── → Next.js (Vercel)
│
└── Chỉ admin/internal, không có end user?
└── → Không cần Next.jsStack theo loại project
Project 1 — Blog / Website công ty
Strapi (EC2) → bài viết, pages, team members
Next.js (Vercel) → render frontend, SEO
RDS PostgreSQL → database
KHÔNG cần: NestJS, Refine, Metabase
Lý do: không có business logic, không cần admin phức tạp
Chi phí: ~$20-30/monthProject 2 — E-commerce đơn giản
Strapi (EC2) → product catalog, categories, banners
NestJS (App Runner)→ orders, payment (Stripe), auth
Next.js (Vercel) → storefront cho end user
Refine (CF Pages) → admin quản lý orders, users
RDS PostgreSQL → database
KHÔNG cần: Metabase (Refine đủ cho basic report)
Chi phí: ~$45-55/monthProject 3 — SaaS platform
Strapi (EC2) → marketing content, docs
NestJS (App Runner)→ core business logic, subscriptions, auth
Next.js (Vercel) → app UI cho end user
Refine (CF Pages) → admin panel cho operation team
Metabase (EC2) → analytics dashboard cho stakeholder
RDS PostgreSQL → primary database
Redis (ElastiCache)→ cache, queue, session
Chi phí: ~$80-120/monthProject 4 — Internal Tool / Back-office thuần túy
NestJS (App Runner)→ API + business logic
Refine (CF Pages) → toàn bộ UI
RDS PostgreSQL → database
KHÔNG cần: Strapi, Next.js
Chi phí: ~$25-35/monthProject 5 — Mobile App Backend
NestJS (App Runner)→ REST API cho mobile app
Strapi (EC2) → app content (banners, notifications, config)
RDS PostgreSQL → database
Refine (CF Pages) → admin panel
KHÔNG cần: Next.js (mobile app là client)
Chi phí: ~$40-50/monthCâu hỏi nên hỏi client trước khi bắt đầu
1. "Ai sẽ dùng hệ thống này hàng ngày?"
→ Non-technical editor → Cần Strapi
→ Developer / ops team → Refine đủ
2. "Ngoài quản lý content, cần làm gì khác?"
→ Chỉ CRUD content → Strapi đủ
→ Có workflow phức tạp → NestJS + Refine
3. "Có end user không? Họ cần làm gì?"
→ Có, cần SEO, đẹp → Next.js (Vercel)
→ Chỉ internal → Không cần Next.js
4. "Cần xem báo cáo gì?"
→ Vài con số, có action → Refine tự build
→ Nhiều chart, read-only→ Metabase
5. "Có tích hợp payment không?"
→ Có → NestJS + Stripe
→ Không → Có thể bỏ NestJS nếu logic đơn giản
6. "Budget infrastructure hàng tháng là bao nhiêu?"
→ < $30/month → Strapi + Next.js only
→ $40-60/month → Full stack cơ bản
→ > $80/month → Full stack + Metabase + RedisNguyên tắc chung
Bắt đầu đơn giản, thêm tool khi thực sự cần.
Mọi project bắt đầu với:
Strapi + NestJS + PostgreSQL (RDS)
Thêm khi cần:
+ Next.js (Vercel) → khi có end-user UI cần SEO
+ Refine (CF Pages) → khi cần admin panel với business actions
+ Metabase → khi client cần tự xem report phức tạp
+ Redis (ElastiCache) → khi cần cache hoặc queue
+ Prisma → thay TypeORM cho project mới
+ S3 + CloudFront → khi Strapi cần media storageOver-engineering từ đầu là lỗi phổ biến nhất của freelancer.
Client trả tiền cho kết quả, không phải cho số lượng tool.
Cập nhật: June 2026