Back to blog

Vercel, Supabase & Hệ Sinh Thái Developer Hiện Đại

vercelsupabasenextjsfullstackcloudtypescript
Vercel, Supabase & Hệ Sinh Thái Developer Hiện Đại

Xây dựng một ứng dụng full-stack trước đây có nghĩa là hàng tuần thiết lập — cung cấp server, cấu hình database, thiết lập xác thực, kết nối dịch vụ email, quản lý deployment. Năm 2026, một thế hệ mới của các nền tảng developer đã thay đổi cuộc chơi.

Vercel deploy frontend của bạn chỉ với một git push. Supabase cung cấp cho bạn PostgreSQL database với xác thực, subscription real-time và storage — tất cả trong vài phút. Xung quanh chúng, một hệ sinh thái các công cụ chuyên biệt xử lý mọi thứ khác: auth, email, thanh toán, caching và nhiều hơn nữa.

Hướng dẫn này khám phá hệ sinh thái developer hiện đại — mỗi công cụ làm gì, cách chúng phối hợp với nhau, và khi nào bạn nên (hoặc không nên) sử dụng stack này.

Bạn Sẽ Học Được Gì

✅ Vercel là gì và cách nó deploy ứng dụng frontend tại edge
✅ Supabase là gì và cách nó thay thế Firebase bằng PostgreSQL
✅ Hệ sinh thái công cụ: Clerk, Drizzle, Upstash, Resend, Stripe và nhiều hơn nữa
✅ Cách thiết kế kiến trúc ứng dụng full-stack với stack hiện đại
✅ Khi nào nên dùng stack này vs cloud truyền thống (AWS/GCP/Azure)
✅ Phân tích chi phí và khả năng free tier
✅ Best practices cho deployment production


Phần 1: Bối Cảnh Nền Tảng Developer Hiện Đại

Điều Gì Đã Thay Đổi?

Các nhà cung cấp cloud truyền thống (AWS, GCP, Azure) rất mạnh mẽ nhưng phức tạp. Deploy một ứng dụng web đơn giản trên AWS bao gồm:

  • Thiết lập EC2 hoặc ECS cho compute
  • Cấu hình RDS cho database
  • Thiết lập CloudFront cho CDN
  • Quản lý IAM roles, VPCs, security groups
  • Thiết lập CI/CD pipelines
  • Cấu hình SSL certificates
  • Monitoring với CloudWatch

Hệ sinh thái developer hiện đại có cách tiếp cận khác: mỗi công cụ làm một việc cực kỳ tốt, với cấu hình tối thiểu và free tier hào phóng.

Các Nguyên Tắc Chính

Nguyên TắcCloud Truyền ThốngHệ Sinh Thái Hiện Đại
Thời gian thiết lậpHàng giờ đến hàng ngàyVài phút
Cấu hìnhThủ công / IaCConvention over configuration
ScalingThủ công / auto-scaling policiesTự động, serverless
Giá cảTrả cho tài nguyên (chạy 24/7)Trả theo sử dụng (scale to zero)
DeploymentCI/CD pipelines, Docker, K8sgit push
DatabaseTự cung cấp và quản lýManaged với branching tích hợp
Free tierHạn chế (dùng thử 12 tháng)Hào phóng (thường đủ cho side projects)

Phần 2: Vercel — Frontend Cloud

Vercel Là Gì?

Vercel là nền tảng frontend cloud được xây dựng bởi nhà sáng tạo Next.js. Nó deploy các ứng dụng web lên mạng lưới edge toàn cầu mà không cần cấu hình.

Về cốt lõi, Vercel là:

  • Một nền tảng deployment (push code → nhận URL)
  • Một mạng lưới edge (ứng dụng chạy gần người dùng trên toàn thế giới)
  • Một serverless runtime (API routes chạy như functions)
  • Một lớp developer experience (preview deployments, analytics, speed insights)

Cách Vercel Hoạt Động

Khi bạn push lên GitHub:

  1. Vercel tự động phát hiện framework (Next.js, Vite, Nuxt, Remix, v.v.)
  2. Chạy build command
  3. Deploy static assets lên CDN toàn cầu
  4. Deploy API routes / server components như serverless functions
  5. Cung cấp preview URL riêng cho mỗi branch/commit

Các Tính Năng Chính Của Vercel

Preview Deployments

Mỗi pull request nhận được URL deployment riêng. Điều này có nghĩa là:

  • Designer có thể review thay đổi UI trước khi merge
  • QA có thể test tính năng trên infrastructure thực
  • Stakeholders có thể xem tiến độ mà không cần chạy code locally
# Mỗi PR tự động nhận URL như:
# https://my-app-git-feature-login-yourteam.vercel.app

Serverless Functions

API routes trong Next.js tự động trở thành serverless functions trên Vercel:

// app/api/users/route.ts — Next.js App Router
import { NextResponse } from 'next/server';
 
export async function GET() {
  const users = await db.query('SELECT * FROM users');
  return NextResponse.json(users);
}
 
export async function POST(request: Request) {
  const body = await request.json();
  const user = await db.insert('users', body);
  return NextResponse.json(user, { status: 201 });
}

Edge Middleware

Chạy code trước khi request đến ứng dụng. Hữu ích cho xác thực, redirects, A/B testing và geolocation:

// middleware.ts — chạy tại edge (nhanh, toàn cầu)
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
 
export function middleware(request: NextRequest) {
  // Kiểm tra xác thực
  const token = request.cookies.get('session');
  if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url));
  }
 
  // Thêm headers geolocation
  const country = request.geo?.country || 'US';
  const response = NextResponse.next();
  response.headers.set('x-user-country', country);
  return response;
}
 
export const config = {
  matcher: ['/dashboard/:path*', '/api/:path*'],
};

Incremental Static Regeneration (ISR)

Phục vụ các trang static tự động cập nhật trong background:

// app/blog/[slug]/page.tsx
export const revalidate = 3600; // Revalidate mỗi giờ
 
export default async function BlogPost({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug);
  return <article>{post.content}</article>;
}

Analytics & Speed Insights

Monitoring hiệu năng tích hợp sẵn:

  • Theo dõi Web Vitals (LCP, FID, CLS, TTFB)
  • Real user monitoring (không phải synthetic benchmarks)
  • Phân tích hiệu năng từng trang
  • Speed Insights với các đề xuất hành động cụ thể

Bảng Giá Vercel

Tính NăngHobby (Miễn Phí)Pro ($20/tháng)Enterprise
DeploymentsKhông giới hạnKhông giới hạnKhông giới hạn
Bandwidth100 GB1 TBTùy chỉnh
Serverless Execution100 GB-hours1000 GB-hoursTùy chỉnh
Preview Deployments✅✅✅
AnalyticsCơ bảnNâng caoNâng cao
Team Members1Không giới hạnKhông giới hạn
Sử Dụng Thương Mại❌✅✅
Password Protection❌✅✅

Mẹo: Gói Hobby đủ hào phóng cho các dự án cá nhân và side projects. Bạn cần Pro cho ứng dụng thương mại hoặc làm việc nhóm.

Vercel vs Các Lựa Chọn Thay Thế

Tính NăngVercelNetlifyCloudflare Pages
Phù Hợp NhấtỨng dụng Next.jsStatic sites, JAMstackỨng dụng edge-first
Hỗ Trợ FrameworkTất cả (Next.js ưu tiên)Tất cảTất cả
Edge Functions✅ Edge Middleware✅ Edge Functions✅ Workers
Serverless Functions✅ (Node.js, Go, Python)✅ (Node.js)✅ (Workers)
Tốc Độ BuildNhanhNhanhNhanh
Bandwidth Miễn Phí100 GB100 GBKhông giới hạn
Hỗ Trợ ISRNativeQua pluginsQua Workers
AnalyticsTích hợp sẵnQua pluginTích hợp sẵn
Giá (Pro)$20/tháng$19/tháng$5/tháng (Workers Paid)

Kết luận: Chọn Vercel cho ứng dụng Next.js (tích hợp tốt nhất). Chọn Netlify cho static sites đơn giản hơn. Chọn Cloudflare Pages cho lựa chọn rẻ nhất với bandwidth không giới hạn.


Phần 3: Supabase — Firebase Alternative Mã Nguồn Mở

Supabase Là Gì?

Supabase là backend-as-a-service (BaaS) mã nguồn mở được xây dựng trên nền PostgreSQL. Nó cung cấp các tính năng mà developer thường cần từ Firebase — nhưng với relational database thực thay vì document store.

Các Tính Năng Cốt Lõi

1. PostgreSQL Database

Supabase cung cấp cho bạn PostgreSQL database đầy đủ — không phải NoSQL store độc quyền. Điều này có nghĩa là:

  • Truy vấn SQL chuẩn
  • Joins, transactions, constraints
  • Extensions (PostGIS, pgvector, pg_cron)
  • Tương thích hoàn toàn với mọi công cụ PostgreSQL
import { createClient } from '@supabase/supabase-js';
 
const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
 
// Truy vấn dữ liệu
const { data: posts, error } = await supabase
  .from('posts')
  .select('*, author:profiles(name, avatar_url)')
  .eq('published', true)
  .order('created_at', { ascending: false })
  .limit(10);
 
// Thêm dữ liệu
const { data, error: insertError } = await supabase
  .from('posts')
  .insert({
    title: 'Bài Viết Mới',
    content: 'Xin chào!',
    author_id: user.id,
  })
  .select()
  .single();

2. REST & GraphQL API Tự Động Sinh

Supabase tự động sinh RESTful APIs từ database schema sử dụng PostgREST. Mỗi table, view và function đều có API endpoint — không cần viết code.

3. Xác Thực (Authentication)

Xác thực tích hợp sẵn với hơn 20 providers:

// Đăng ký bằng email/password
const { data, error } = await supabase.auth.signUp({
  email: '[email protected]',
  password: 'securepassword123',
});
 
// OAuth (Google, GitHub, Discord, v.v.)
const { data, error } = await supabase.auth.signInWithOAuth({
  provider: 'google',
  options: {
    redirectTo: 'https://yourapp.com/auth/callback',
  },
});
 
// Magic link (không cần mật khẩu)
const { data, error } = await supabase.auth.signInWithOtp({
  email: '[email protected]',
});
 
// Lấy user hiện tại
const { data: { user } } = await supabase.auth.getUser();

4. Row Level Security (RLS)

RLS là tính năng killer. Nó cho phép bạn định nghĩa ai có thể truy cập những hàng nào trực tiếp trong database — không cần backend middleware:

-- Bật RLS trên bảng posts
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
 
-- Ai cũng có thể đọc bài viết đã xuất bản
CREATE POLICY "Public can read published posts"
  ON posts FOR SELECT
  USING (published = true);
 
-- Users chỉ có thể thêm bài viết của chính mình
CREATE POLICY "Users can insert own posts"
  ON posts FOR INSERT
  WITH CHECK (auth.uid() = author_id);
 
-- Users chỉ có thể cập nhật bài viết của chính mình
CREATE POLICY "Users can update own posts"
  ON posts FOR UPDATE
  USING (auth.uid() = author_id)
  WITH CHECK (auth.uid() = author_id);
 
-- Users chỉ có thể xóa bài viết của chính mình
CREATE POLICY "Users can delete own posts"
  ON posts FOR DELETE
  USING (auth.uid() = author_id);

Với RLS, code phía client có thể truy vấn database trực tiếp — các quy tắc bảo mật được thực thi tại tầng database:

// Client-side: điều này AN TOÀN vì RLS được thực thi
// User sẽ chỉ thấy bài viết đã xuất bản (hoặc của chính họ)
const { data: posts } = await supabase
  .from('posts')
  .select('*');

5. Realtime Subscriptions

Lắng nghe thay đổi database theo thời gian thực:

// Subscribe để nhận bài viết mới
const channel = supabase
  .channel('public:posts')
  .on(
    'postgres_changes',
    { event: 'INSERT', schema: 'public', table: 'posts' },
    (payload) => {
      console.log('Bài viết mới:', payload.new);
    }
  )
  .subscribe();
 
// Presence (ai đang online)
const presenceChannel = supabase.channel('room-1');
presenceChannel
  .on('presence', { event: 'sync' }, () => {
    const state = presenceChannel.presenceState();
    console.log('Users online:', Object.keys(state).length);
  })
  .subscribe(async (status) => {
    if (status === 'SUBSCRIBED') {
      await presenceChannel.track({ user_id: user.id, name: user.name });
    }
  });

6. Edge Functions

Serverless functions viết bằng TypeScript/Deno chạy gần người dùng:

// supabase/functions/send-welcome-email/index.ts
import { serve } from 'https://deno.land/[email protected]/http/server.ts';
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';
 
serve(async (req) => {
  const { email, name } = await req.json();
 
  // Gửi email qua Resend
  const res = await fetch('https://api.resend.com/emails', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${Deno.env.get('RESEND_API_KEY')}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      from: '[email protected]',
      to: email,
      subject: `Chào mừng, ${name}!`,
      html: `<h1>Chào mừng đến với ứng dụng, ${name}!</h1>`,
    }),
  });
 
  return new Response(JSON.stringify({ success: true }), {
    headers: { 'Content-Type': 'application/json' },
  });
});

7. Storage

Lưu trữ file với access control gắn với database policies:

// Upload file
const { data, error } = await supabase.storage
  .from('avatars')
  .upload(`${user.id}/profile.png`, file, {
    cacheControl: '3600',
    upsert: true,
  });
 
// Lấy public URL
const { data: { publicUrl } } = supabase.storage
  .from('avatars')
  .getPublicUrl(`${user.id}/profile.png`);

Supabase vs Các Lựa Chọn Thay Thế

Tính NăngSupabaseFirebaseNeonPlanetScale
DatabasePostgreSQLFirestore (NoSQL)PostgreSQLMySQL
Mã Nguồn Mở✅❌❌❌
Hỗ Trợ SQLSQL đầy đủKhông SQLSQL đầy đủSQL đầy đủ
AuthTích hợp sẵnTích hợp sẵn❌ (dùng bên thứ ba)❌ (dùng bên thứ ba)
RealtimeTích hợp sẵnTích hợp sẵn❌❌
Edge Functions✅ (Deno)✅ (Node.js)❌❌
StorageTích hợp sẵnTích hợp sẵn❌❌
RLS✅Security Rules✅ (thủ công)❌
Branching✅❌✅✅
Self-Host✅❌❌❌
Free Tier DB500 MB1 GB512 MB5 GB

Kết luận: Chọn Supabase cho backend hoàn chỉnh với auth và realtime. Chọn Firebase nếu bạn thích NoSQL và hệ sinh thái Google. Chọn Neon hoặc PlanetScale nếu bạn chỉ cần managed database (không cần auth/storage).

Bảng Giá Supabase

Tính NăngMiễn PhíPro ($25/tháng)Team ($599/tháng)
Database500 MB8 GB16 GB
Bandwidth5 GB250 GB500 GB
Storage1 GB100 GB200 GB
Edge Functions500K invocations2M invocations5M invocations
Auth MAUs50,000100,000100,000+
Realtime200 concurrent500 concurrentTùy chỉnh
Hỗ TrợCộng đồngEmailƯu tiên

Phần 4: Hệ Sinh Thái — Các Công Cụ Phối Hợp Cùng Nhau

Stack hiện đại không chỉ là Vercel + Supabase. Đó là cả một hệ sinh thái các công cụ chuyên biệt tích hợp liền mạch.

Xác Thực: Clerk & Auth.js

Mặc dù Supabase có auth tích hợp sẵn, đôi khi bạn cần các tính năng nâng cao hơn:

Clerk

Clerk cung cấp các UI component xác thực drop-in với các tính năng nâng cao:

// app/layout.tsx — Clerk với Next.js
import { ClerkProvider } from '@clerk/nextjs';
 
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <ClerkProvider>
      <html>
        <body>{children}</body>
      </html>
    </ClerkProvider>
  );
}
// app/dashboard/page.tsx — route được bảo vệ
import { auth } from '@clerk/nextjs/server';
import { redirect } from 'next/navigation';
 
export default async function DashboardPage() {
  const { userId } = await auth();
  if (!userId) redirect('/sign-in');
 
  return <h1>Chào mừng đến dashboard của bạn</h1>;
}

Clerk phù hợp khi bạn cần:

  • UI components đăng nhập/đăng ký sẵn có
  • Xác thực đa yếu tố (MFA)
  • Quản lý tổ chức/nhóm
  • Dashboard quản lý phiên đăng nhập
  • Giả mạo user cho hỗ trợ kỹ thuật

Auth.js (NextAuth)

Auth.js là lựa chọn mã nguồn mở cho xác thực Next.js:

// auth.ts — Cấu hình Auth.js
import NextAuth from 'next-auth';
import GitHub from 'next-auth/providers/github';
import Google from 'next-auth/providers/google';
 
export const { handlers, auth, signIn, signOut } = NextAuth({
  providers: [
    GitHub({ clientId: process.env.GITHUB_ID!, clientSecret: process.env.GITHUB_SECRET! }),
    Google({ clientId: process.env.GOOGLE_ID!, clientSecret: process.env.GOOGLE_SECRET! }),
  ],
});
Tính NăngSupabase AuthClerkAuth.js
GiáMiễn phí (50K MAUs)Miễn phí (10K MAUs)Miễn phí (mã nguồn mở)
UI ComponentsCơ bảnĐẹp, sẵn cóTự xây dựng
MFA✅✅Qua providers
Tổ Chức❌✅❌
Self-hosted✅❌✅
Phù Hợp NhấtDự án SupabaseAuth UX hoàn thiệnAuth flow tùy chỉnh

Database ORMs: Drizzle & Prisma

Khi làm việc với PostgreSQL của Supabase (hoặc Neon), bạn sẽ muốn một ORM type-safe:

Drizzle ORM

Drizzle là ORM nhẹ, type-safe không tạo overhead:

// db/schema.ts — Drizzle schema
import { pgTable, serial, text, boolean, timestamp } from 'drizzle-orm/pg-core';
 
export const posts = pgTable('posts', {
  id: serial('id').primaryKey(),
  title: text('title').notNull(),
  content: text('content').notNull(),
  published: boolean('published').default(false),
  authorId: text('author_id').notNull(),
  createdAt: timestamp('created_at').defaultNow(),
});
// Truy vấn với type safety đầy đủ
import { db } from './db';
import { posts } from './db/schema';
import { eq, desc } from 'drizzle-orm';
 
const publishedPosts = await db
  .select()
  .from(posts)
  .where(eq(posts.published, true))
  .orderBy(desc(posts.createdAt))
  .limit(10);
// TypeScript biết chính xác shape của publishedPosts

Prisma

Prisma sử dụng cách tiếp cận schema-first với SDL riêng:

// prisma/schema.prisma
model Post {
  id        Int      @id @default(autoincrement())
  title     String
  content   String
  published Boolean  @default(false)
  authorId  String
  createdAt DateTime @default(now())
  author    User     @relation(fields: [authorId], references: [id])
}
// Truy vấn với Prisma
const publishedPosts = await prisma.post.findMany({
  where: { published: true },
  orderBy: { createdAt: 'desc' },
  take: 10,
  include: { author: true },
});
Tính NăngDrizzlePrisma
Cách tiếp cậnCode-first (TypeScript)Schema-first (SDL)
Bundle Size~50 KB~800 KB
Cú pháp giống SQL✅❌ (API riêng)
MigrationsSQL migrationsPrisma Migrate
Raw SQLDễ dàng$queryRaw
Edge Support✅✅ (với adapters)
Learning CurveThấp (nếu biết SQL)Thấp (API trực quan)
Phù Hợp NhấtDev thành thạo SQLTeam muốn abstraction

Caching & Queues: Upstash

Upstash cung cấp serverless Redis và Kafka — trả theo request, scale to zero:

// Rate limiting với Upstash
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';
 
const ratelimit = new Ratelimit({
  redis: Redis.fromEnv(),
  limiter: Ratelimit.slidingWindow(10, '10 s'), // 10 requests mỗi 10 giây
});
 
// Trong API route của bạn
export async function POST(request: Request) {
  const ip = request.headers.get('x-forwarded-for') || 'unknown';
  const { success, limit, remaining } = await ratelimit.limit(ip);
 
  if (!success) {
    return new Response('Quá nhiều request', { status: 429 });
  }
 
  // Xử lý request...
}
// Caching với Upstash Redis
import { Redis } from '@upstash/redis';
 
const redis = Redis.fromEnv();
 
export async function getPost(slug: string) {
  // Kiểm tra cache trước
  const cached = await redis.get<Post>(`post:${slug}`);
  if (cached) return cached;
 
  // Lấy từ database
  const post = await db.query.posts.findFirst({
    where: eq(posts.slug, slug),
  });
 
  // Cache trong 1 giờ
  await redis.set(`post:${slug}`, post, { ex: 3600 });
  return post;
}

Upstash cũng cung cấp:

  • QStash — hàng đợi tin nhắn serverless (tác vụ trì hoãn, webhooks)
  • Vector — vector database serverless (cho ứng dụng AI/RAG)
  • Workflow — serverless workflows bền vững

Email: Resend & React Email

Resend là email API ưu tiên developer. React Email cho phép bạn xây dựng email bằng React components:

// emails/welcome.tsx — React Email template
import { Html, Head, Body, Container, Text, Button } from '@react-email/components';
 
export function WelcomeEmail({ name, loginUrl }: { name: string; loginUrl: string }) {
  return (
    <Html>
      <Head />
      <Body style={{ fontFamily: 'sans-serif', background: '#f4f4f5' }}>
        <Container style={{ maxWidth: '480px', margin: '0 auto', padding: '20px' }}>
          <Text style={{ fontSize: '24px', fontWeight: 'bold' }}>
            Chào mừng, {name}!
          </Text>
          <Text>Cảm ơn bạn đã đăng ký. Nhấn bên dưới để bắt đầu:</Text>
          <Button
            href={loginUrl}
            style={{ background: '#000', color: '#fff', padding: '12px 24px', borderRadius: '6px' }}
          >
            Đến Dashboard
          </Button>
        </Container>
      </Body>
    </Html>
  );
}
// Gửi email với Resend
import { Resend } from 'resend';
import { WelcomeEmail } from '@/emails/welcome';
 
const resend = new Resend(process.env.RESEND_API_KEY);
 
await resend.emails.send({
  from: 'App <[email protected]>',
  to: user.email,
  subject: 'Chào Mừng Đến Với Ứng Dụng!',
  react: WelcomeEmail({ name: user.name, loginUrl: 'https://yourapp.com/dashboard' }),
});

Thanh Toán: Stripe

Stripe vẫn là tiêu chuẩn cho thanh toán. Với Next.js và stack hiện đại:

// app/api/checkout/route.ts — Tạo Stripe checkout session
import Stripe from 'stripe';
 
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
 
export async function POST(request: Request) {
  const { priceId } = await request.json();
 
  const session = await stripe.checkout.sessions.create({
    mode: 'subscription',
    payment_method_types: ['card'],
    line_items: [{ price: priceId, quantity: 1 }],
    success_url: `${process.env.NEXT_PUBLIC_URL}/dashboard?success=true`,
    cancel_url: `${process.env.NEXT_PUBLIC_URL}/pricing`,
  });
 
  return Response.json({ url: session.url });
}
// app/api/webhooks/stripe/route.ts — Xử lý Stripe webhooks
import Stripe from 'stripe';
 
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
 
export async function POST(request: Request) {
  const body = await request.text();
  const signature = request.headers.get('stripe-signature')!;
 
  const event = stripe.webhooks.constructEvent(
    body,
    signature,
    process.env.STRIPE_WEBHOOK_SECRET!
  );
 
  switch (event.type) {
    case 'checkout.session.completed':
      const session = event.data.object;
      // Kích hoạt subscription của user trong database
      await activateSubscription(session.customer as string);
      break;
 
    case 'customer.subscription.deleted':
      const subscription = event.data.object;
      // Hủy kích hoạt subscription
      await deactivateSubscription(subscription.customer as string);
      break;
  }
 
  return Response.json({ received: true });
}

Các Công Cụ Đáng Chú Ý Khác

Công CụDanh MụcChức NăngFree Tier
NeonDatabaseServerless PostgreSQL với branching512 MB
TursoDatabaseSQLite tại edge (libSQL)9 GB storage
Lemon SqueezyThanh toánThay thế Stripe (xử lý thuế/billing)Miễn phí (phí theo giao dịch)
InngestBackground JobsEvent-driven serverless functions25K events/tháng
Trigger.devBackground JobsBackground jobs serverless mã nguồn mở50K runs/tháng
PostHogAnalyticsProduct analytics, feature flags, session replay1M events/tháng
SentryMonitoringError tracking, performance monitoring5K errors/tháng

Phần 5: Xây Dựng Ứng Dụng Full-Stack Với Stack Hiện Đại

Hãy xây dựng kiến trúc thực tế sử dụng các công cụ này cùng nhau. Chúng ta sẽ thiết kế một ứng dụng SaaS với xác thực, database, thanh toán và email.

Tổng Quan Kiến Trúc

Thiết Lập Dự Án

# Tạo dự án Next.js
npx create-next-app@latest my-saas-app --typescript --tailwind --app --src-dir
 
cd my-saas-app
 
# Cài đặt các package hệ sinh thái
npm install @supabase/supabase-js @clerk/nextjs @upstash/redis @upstash/ratelimit resend stripe
npm install drizzle-orm postgres
npm install -D drizzle-kit

Biến Môi Trường

# .env.local
 
# Vercel (tự động điền khi deploy)
NEXT_PUBLIC_URL=http://localhost:3000
 
# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://xxxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...
SUPABASE_SERVICE_ROLE_KEY=eyJ...
 
# Clerk
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...
 
# Upstash
UPSTASH_REDIS_REST_URL=https://xxxxx.upstash.io
UPSTASH_REDIS_REST_TOKEN=AXxx...
 
# Resend
RESEND_API_KEY=re_...
 
# Stripe
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...

Database Schema Với Drizzle

// src/db/schema.ts
import { pgTable, text, timestamp, boolean, integer } from 'drizzle-orm/pg-core';
 
export const profiles = pgTable('profiles', {
  id: text('id').primaryKey(), // Clerk user ID
  email: text('email').notNull().unique(),
  name: text('name'),
  avatarUrl: text('avatar_url'),
  stripeCustomerId: text('stripe_customer_id'),
  plan: text('plan').default('free'), // 'free' | 'pro' | 'team'
  createdAt: timestamp('created_at').defaultNow(),
  updatedAt: timestamp('updated_at').defaultNow(),
});
 
export const projects = pgTable('projects', {
  id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
  name: text('name').notNull(),
  description: text('description'),
  ownerId: text('owner_id').notNull().references(() => profiles.id),
  isPublic: boolean('is_public').default(false),
  createdAt: timestamp('created_at').defaultNow(),
});
 
export const apiKeys = pgTable('api_keys', {
  id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
  projectId: text('project_id').notNull().references(() => projects.id),
  name: text('name').notNull(),
  key: text('key').notNull().unique(),
  lastUsedAt: timestamp('last_used_at'),
  requestCount: integer('request_count').default(0),
  createdAt: timestamp('created_at').defaultNow(),
});

Authentication Middleware

// src/middleware.ts
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
 
const isPublicRoute = createRouteMatcher([
  '/',
  '/pricing',
  '/blog(.*)',
  '/sign-in(.*)',
  '/sign-up(.*)',
  '/api/webhooks(.*)',
]);
 
export default clerkMiddleware(async (auth, request) => {
  if (!isPublicRoute(request)) {
    await auth.protect();
  }
});
 
export const config = {
  matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)'],
};

API Route Với Rate Limiting

// src/app/api/projects/route.ts
import { auth } from '@clerk/nextjs/server';
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';
import { db } from '@/db';
import { projects } from '@/db/schema';
import { eq } from 'drizzle-orm';
 
const ratelimit = new Ratelimit({
  redis: Redis.fromEnv(),
  limiter: Ratelimit.slidingWindow(20, '60 s'),
});
 
export async function GET() {
  const { userId } = await auth();
  if (!userId) return new Response('Unauthorized', { status: 401 });
 
  // Rate limit
  const { success } = await ratelimit.limit(userId);
  if (!success) return new Response('Quá nhiều request', { status: 429 });
 
  const userProjects = await db
    .select()
    .from(projects)
    .where(eq(projects.ownerId, userId));
 
  return Response.json(userProjects);
}
 
export async function POST(request: Request) {
  const { userId } = await auth();
  if (!userId) return new Response('Unauthorized', { status: 401 });
 
  const { name, description } = await request.json();
 
  const [project] = await db
    .insert(projects)
    .values({ name, description, ownerId: userId })
    .returning();
 
  return Response.json(project, { status: 201 });
}

Deploy Lên Vercel

# Cài đặt Vercel CLI
npm install -g vercel
 
# Liên kết với dự án Vercel
vercel link
 
# Thêm biến môi trường
vercel env add SUPABASE_SERVICE_ROLE_KEY
vercel env add CLERK_SECRET_KEY
vercel env add STRIPE_SECRET_KEY
# ... (hoặc thêm qua Vercel Dashboard → Settings → Environment Variables)
 
# Deploy
vercel --prod
# Hoặc chỉ cần push lên GitHub — Vercel tự động deploy

Phần 6: Khi Nào Nên Dùng Stack Này vs Cloud Truyền Thống

Framework Quyết Định

Chọn Stack Hiện Đại Khi

✅ Xây dựng MVP hoặc side project — ship trong vài ngày, không phải vài tuần
✅ Bạn là developer solo hoặc team nhỏ — ít hạ tầng cần quản lý
✅ Ứng dụng là web application (SaaS, dashboard, marketplace, blog)
✅ Bạn muốn free tier hào phóng — xây dựng mà không tốn tiền
✅ Bạn ưu tiên developer experience — ít boilerplate, nhiều building hơn
✅ Bạn cần preview environments — test mỗi PR trên infrastructure thực

Chọn Cloud Truyền Thống (AWS/GCP/Azure) Khi

✅ Bạn cần networking tùy chỉnh (VPCs, VPNs, private subnets)
✅ Bạn chạy ML workloads, GPU instances, hoặc compute nặng
✅ Bạn cần compliance nghiêm ngặt (HIPAA, FedRAMP, data residency)
✅ Bạn có team DevOps/platform chuyên dụng
✅ Bạn cần Kafka, Elasticsearch, hoặc dịch vụ chuyên biệt ở quy mô lớn
✅ Bạn xử lý khối lượng dữ liệu khổng lồ (data pipelines, ETL)

Hạn Chế Của Stack Hiện Đại

Hạn ChếChi TiếtGiải Pháp
Vendor lock-inTính năng riêng Vercel (Edge Middleware, ISR)Giữ logic cốt lõi không phụ thuộc framework
Cold startsServerless functions có độ trễ khởi động (50-500ms)Dùng edge functions, giữ functions warm
Chi phí egressBandwidth có thể đắt khi scaleCache tích cực, tối ưu payload
Giới hạn scalingServerless có giới hạn concurrencyLiên hệ sales để tăng giới hạn
Compute hạn chếKhông hỗ trợ process chạy lâu, không GPUDùng cloud truyền thống cho compute nặng
Kích thước databaseFree tier nhỏ (500MB-5GB)Nâng cấp lên plan trả phí khi cần

Lộ Trình Di Chuyển

Nếu bạn vượt qua stack hiện đại, việc di chuyển khá đơn giản:

Vercel → Self-hosted Next.js:

# Next.js hoạt động mọi nơi có Node.js
npm run build
npm run start  # Standard Node.js server
 
# Hoặc dùng Docker
docker build -t my-app .
docker run -p 3000:3000 my-app

Supabase → Self-hosted PostgreSQL:

  • Supabase là mã nguồn mở — bạn có thể self-host toàn bộ stack
  • Hoặc chỉ lấy PostgreSQL database và kết nối trực tiếp
  • Drizzle/Prisma schemas hoạt động với mọi PostgreSQL instance

Clerk → Auth.js hoặc custom auth:

  • Export dữ liệu user qua Clerk API
  • Implement Auth.js với cùng OAuth providers
  • Di chuyển sessions trong giai đoạn chuyển tiếp

Phần 7: Phân Tích Chi Phí

Stack Free Tier (MVP / Side Project)

Xây dựng ứng dụng hoàn chỉnh với $0/tháng:

Công CụFree TierBạn Nhận Được
VercelHobbyKhông giới hạn deployments, 100 GB bandwidth
SupabaseMiễn phí500 MB database, 50K auth users, 1 GB storage
ClerkMiễn phí10,000 monthly active users
UpstashMiễn phí10K commands/ngày Redis, 500 messages/ngày QStash
ResendMiễn phí3,000 emails/tháng
StripeMiễn phíChỉ trả phí mỗi giao dịch (2.9% + $0.30)
SentryMiễn phí5,000 errors/tháng
PostHogMiễn phí1M events/tháng
Tổng$0/thángĐủ cho hàng nghìn users

Stack Tăng Trưởng ($50-100/tháng)

Khi bạn vượt qua free tier:

Công CụGóiChi Phí
VercelPro$20/tháng
SupabasePro$25/tháng
ClerkPro$25/tháng (10K+ MAUs)
UpstashPay-as-you-go~$5-10/tháng
ResendPro$20/tháng
Tổng~$95-100/tháng

Stack này có thể xử lý hàng chục nghìn active users với hiệu năng tuyệt vời.

So Sánh Với Cloud Truyền Thống

Stack Hiện ĐạiAWS Tương Đương
ComputeVercel ($20)EC2 / ECS (~$30-100)
DatabaseSupabase ($25)RDS (~$30-60)
AuthClerk ($25)Cognito (~$10-50) + thời gian dev
CDNBao gồm trong VercelCloudFront (~$10-30)
RedisUpstash ($5)ElastiCache (~$15-50)
EmailResend ($20)SES (~$5) + thời gian dev
MonitoringPostHog/Sentry (miễn phí)CloudWatch (~$10-30)
Thời gian DevOpsTối thiểuĐáng kể
Tổng~$95/tháng~$110-320/tháng + lương DevOps

Stack hiện đại thường có chi phí thấp hơn và yêu cầu ít chuyên môn DevOps hơn đáng kể.


Phần 8: Best Practices

1. Quản Lý Biến Môi Trường

# Sử dụng hệ thống biến môi trường của Vercel
# Tách riêng giá trị cho Production, Preview và Development
 
# .env.local → Chỉ phát triển local (không bao giờ commit)
# Vercel Dashboard → Biến môi trường Production
# Vercel Dashboard → Biến môi trường Preview (cho PR deployments)

Quy tắc: Không bao giờ commit secrets vào Git. Dùng .env.local cho development và dashboard Vercel cho production.

2. Database Branching

Cả Supabase và Neon đều hỗ trợ database branching — tạo bản sao database biệt lập cho mỗi PR:

# Supabase branching (qua CLI)
supabase db branch create feature-new-schema
 
# Neon branching (qua CLI hoặc API)
neonctl branches create --name feature-new-schema

Điều này có nghĩa mỗi preview deployment trên Vercel có thể có database riêng với dữ liệu test.

3. Type Safety Xuyên Suốt Stack

Dùng TypeScript ở mọi nơi và chia sẻ types:

// types/database.ts — sinh từ schema
import { InferSelectModel, InferInsertModel } from 'drizzle-orm';
import { profiles, projects } from '@/db/schema';
 
export type Profile = InferSelectModel<typeof profiles>;
export type NewProfile = InferInsertModel<typeof profiles>;
export type Project = InferSelectModel<typeof projects>;
export type NewProject = InferInsertModel<typeof projects>;

4. Pattern Xử Lý Lỗi

// lib/safe-action.ts — server actions type-safe
export async function safeAction<T>(
  fn: () => Promise<T>
): Promise<{ data: T; error: null } | { data: null; error: string }> {
  try {
    const data = await fn();
    return { data, error: null };
  } catch (err) {
    console.error(err);
    return { data: null, error: err instanceof Error ? err.message : 'Lỗi không xác định' };
  }
}

5. Monitoring & Observability

// Instrument ứng dụng với Sentry
// next.config.mjs
import { withSentryConfig } from '@sentry/nextjs';
 
export default withSentryConfig(nextConfig, {
  org: 'your-org',
  project: 'your-project',
  silent: true,
});

6. Checklist Bảo Mật

  • ✅ Bật RLS trên tất cả bảng Supabase
  • ✅ Dùng SUPABASE_SERVICE_ROLE_KEY chỉ trong code server-side
  • ✅ Xác thực webhook signatures (Stripe, Clerk)
  • ✅ Rate limit tất cả API endpoints
  • ✅ Đặt CORS headers phù hợp
  • ✅ Dùng cookies httpOnly cho auth tokens
  • ✅ Không bao giờ expose secret keys trong code client-side

Kết Luận

Hệ sinh thái developer hiện đại đã thay đổi căn bản cách chúng ta xây dựng ứng dụng web. Thay vì dành hàng tuần cho hạ tầng, bạn có thể có ứng dụng full-stack sẵn sàng production được deploy trong vài giờ.

Đây là tóm tắt cuối cùng:

Thành PhầnCông Cụ Đề XuấtLý Do
DeploymentVercelTích hợp Next.js tốt nhất, preview deployments
DatabaseSupabasePostgreSQL + auth + realtime trong một package
AuthClerk hoặc Supabase AuthClerk cho UX nâng cao, Supabase Auth cho đơn giản
ORMDrizzleNhẹ, type-safe, giống SQL
CachingUpstash RedisServerless, pay-per-request
EmailResendAPI ưu tiên developer, React Email templates
Thanh ToánStripeTiêu chuẩn ngành, docs tuyệt vời
AnalyticsPostHogMã nguồn mở, free tier hào phóng
MonitoringSentryError tracking, performance monitoring

Sự thật thực tế: Stack này lý tưởng cho MVPs, startups, side projects và ứng dụng SaaS vừa và nhỏ. Nó cho phép bạn tập trung vào xây dựng sản phẩm thay vì quản lý hạ tầng. Khi bạn vượt qua nó, lộ trình di chuyển rõ ràng — và lúc đó bạn sẽ có doanh thu để biện minh cho khoản đầu tư.

Bắt đầu với free tier. Ship nhanh. Scale khi cần.


Đọc Thêm

📬 Đăng ký nhận bản tin

Nhận những bài viết mới nhất qua email mỗi tuần. Không spam, hủy đăng ký bất cứ lúc nào.

Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất cứ lúc nào.

💬 Comments

Đăng nhập để bình luận

Chúng tôi sẽ không đăng bất cứ điều gì mà không có sự cho phép của bạn.