Xác thực là nền tảng của bất kỳ ứng dụng web hiện đại nào, nhưng việc thiết lập nó vẫn là một trong những thách thức tốn thời gian nhất mà các nhà phát triển phải đối mặt. Hãy đến với Better Auth API—một giải pháp xác thực không phụ thuộc vào framework, hứa hẹn sẽ thay đổi cách chúng ta triển khai quản lý người dùng. Trong hướng dẫn toàn diện này, chúng ta sẽ cùng xây dựng một ứng dụng full-stack hoàn chỉnh để minh họa sức mạnh và sự đơn giản của Better Auth, sử dụng Bun để đạt hiệu suất cực nhanh.
Bạn muốn một nền tảng tích hợp, tất cả trong một để Đội ngũ Phát triển của bạn làm việc cùng nhau với năng suất tối đa?
Apidog đáp ứng mọi yêu cầu của bạn, và thay thế Postman với mức giá phải chăng hơn nhiều!
Better Auth API là gì?
Better Auth API là một framework xác thực hiện đại, mã nguồn mở được thiết kế để hoạt động liền mạch trên mọi môi trường JavaScript. Không giống như các thư viện xác thực truyền thống ràng buộc bạn vào các hệ sinh thái cụ thể, Better Auth cung cấp một API thống nhất thích ứng với ngăn xếp công nghệ của bạn—cho dù bạn đang sử dụng React, Vue, Svelte hay vanilla JavaScript ở frontend, và Node.js, Bun, hay Deno ở backend. Kiến trúc dựa trên plugin của nó hỗ trợ nhiều chiến lược xác thực, từ email/mật khẩu truyền thống đến các nhà cung cấp OAuth, passkey và magic link, tất cả đều duy trì tính an toàn kiểu dữ liệu và trải nghiệm nhà phát triển tuyệt vời.

Bắt đầu với Better Auth: Điều kiện tiên quyết và Thiết lập dự án
Trước khi đi sâu vào code, hãy đảm bảo bạn đã cài đặt những thứ sau:
- Bun 1.0+: Chúng ta sẽ sử dụng Bun trong suốt hướng dẫn này vì tốc độ vượt trội và các công cụ hiện đại của nó.
- Node.js 18+: Bắt buộc cho môi trường chạy backend.
- Một trình soạn thảo mã: VS Code được khuyến nghị để hỗ trợ TypeScript.

Mặc dù thiết lập này hoạt động hoàn hảo với npm, chúng ta sẽ trình bày quy trình làm việc của Bun, vốn cung cấp tốc độ cài đặt gói nhanh hơn 3-5 lần và trải nghiệm phát triển tinh gọn hơn.
Xây dựng dự án mẫu: Triển khai từng bước
Hãy tạo một hệ thống xác thực thực tế với frontend React và backend Express, hoàn chỉnh với khả năng lưu trữ dữ liệu vào cơ sở dữ liệu.
Bước 1: Thiết lập Backend với Express và Drizzle ORM
1. Khởi tạo Dự án Backend
Đầu tiên, tạo và vào thư mục backend của bạn:
mkdir better-auth-backend
cd better-auth-backend
bun init -y
2. Cài đặt các phụ thuộc
Chúng ta sẽ cần Express cho máy chủ, Better Auth cho xác thực và Drizzle ORM để quản lý cơ sở dữ liệu:
bun add express better-auth drizzle-orm
bun add -D @types/bun @types/express drizzle-kit
3. Cấu hình biến môi trường
Tạo một tệp .env để lưu trữ cấu hình nhạy cảm:
BETTER_AUTH_SECRET=your-secret-key-here # Generate with: openssl rand -base64 32
BETTER_AUTH_URL=http://localhost:3000
DATABASE_URL=local.db
BETTER_AUTH_SECRET cũng có thể được tạo tại trang web better-auth.
4. Tạo Schema cơ sở dữ liệu với Drizzle ORM
Better Auth hoạt động tốt nhất với Drizzle, cung cấp hỗ trợ TypeScript tuyệt vời và tránh các vấn đề về module gốc. Tạo tệp src/db/schema.ts:
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
export const user = sqliteTable("user", {
id: text("id").primaryKey(),
name: text('name').notNull(),
email: text('email').notNull().unique(),
emailVerified: integer('email_verified', { mode: 'boolean' }).notNull().default(false),
image: text('image'),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull(),
});
export const session = sqliteTable("session", {
id: text("id").primaryKey(),
expiresAt: integer('expires_at', { mode: 'timestamp' }).notNull(),
token: text('token').notNull().unique(),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
userId: text('user_id').notNull(),
});
export const account = sqliteTable("account", {
id: text("id").primaryKey(),
accountId: text('account_id').notNull(),
providerId: text('provider_id').notNull(),
userId: text('user_id').notNull(),
accessToken: text('access_token'),
refreshToken: text('refresh_token'),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
});
export const verification = sqliteTable("verification", {
id: text("id").primaryKey(),
identifier: text('identifier').notNull(),
value: text('value').notNull(),
expiresAt: integer('expires_at', { mode: 'timestamp' }).notNull(),
});
5. Cấu hình kết nối cơ sở dữ liệu
Tạo tệp src/db/index.ts sử dụng liên kết SQLite gốc của Bun:
import { Database } from "bun:sqlite";
import { drizzle } from "drizzle-orm/bun-sqlite";
const sqlite = new Database("local.db");
export const db = drizzle(sqlite);
6. Thiết lập cấu hình Better Auth
Tạo tệp src/lib/auth.ts để cấu hình Better Auth API:
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "../db";
import { user, session, account, verification } from "../db/schema";
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: "sqlite",
schema: {
user: user,
session: session,
account: account,
verification: verification,
},
}),
emailAndPassword: {
enabled: true,
},
trustedOrigins: ["http://localhost:5173"],
});
7. Tạo Máy chủ Express
Trong tệp src/index.ts, gắn trình xử lý Better Auth API:
import express from "express";
import cors from "cors";
import { toNodeHandler } from "better-auth/node";
import { auth } from "./lib/auth";
const app = express();
const PORT = process.env.PORT || 3000;
app.use(
cors({
origin: "http://localhost:5173",
credentials: true,
})
);
// Gắn Better Auth API tại /api/auth
app.use("/api/auth", toNodeHandler(auth));
app.use(express.json());
app.get("/api/me", async (req, res) => {
const session = await auth.api.getSession({
headers: req.headers,
});
if (!session) {
return res.status(401).json({ error: "Unauthorized" });
}
res.json({ user: session.user });
});
app.listen(PORT, () => {
console.log(`Máy chủ đang chạy tại http://localhost:${PORT}`);
});
8. Chạy Migration cơ sở dữ liệu
Tạo tệp drizzle.config.ts trong thư mục gốc backend:
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "sqlite",
schema: "./src/db/schema.ts",
out: "./drizzle",
dbCredentials: {
url: "local.db",
},
});
Thực thi migration để tạo các bảng:
bunx drizzle-kit push

Bước 2: Thiết lập Frontend với React và Vite
1. Tạo Ứng dụng React
Trong một terminal mới, khởi tạo frontend:
bun create vite better-auth-frontend --template react-ts
cd better-auth-frontend
2. Cài đặt các phụ thuộc
bun add better-auth
3. Cấu hình Tailwind CSS (Cập nhật V4)
Sau khi Tailwind CSS v4, yêu cầu một thiết lập khác. Cài đặt các gói mới:
bun add -D tailwindcss postcss @tailwindcss/postcss
Tạo tệp tailwind.config.js trong thư mục gốc của dự án:
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
Tạo tệp postcss.config.js:
export default {
plugins: {
"@tailwindcss/postcss": {},
},
}
Tạo tệp src/index.css:
@import "tailwindcss";
body {
margin: 0;
font-family: system-ui, -apple-system, sans-serif;
}
4. Thiết lập Client Better Auth
Tạo tệp src/lib/auth-client.ts:
import { createAuthClient } from "better-auth/react";
export const authClient = createAuthClient({
baseURL: "http://localhost:3000",
});
export const { signIn, signUp, useSession } = authClient;
5. Xây dựng Giao diện người dùng xác thực
Thay thế tệp src/App.tsx bằng một giao diện xác thực hoàn chỉnh:
import { useState } from 'react';
import { useSession, signIn, signUp } from './lib/auth-client';
function App() {
const { data: session, isPending } = useSession();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [name, setName] = useState('');
const handleSignUp = async (e: React.FormEvent) => {
e.preventDefault();
await signUp.email({
name,
email,
password,
callbackURL: '/',
});
};
const handleSignIn = async (e: React.FormEvent) => {
e.preventDefault();
await signIn.email({
email,
password,
});
};
const handleSignOut = async () => {
await authClient.signOut();
};
if (isPending) return <div className="flex items-center justify-center min-h-screen">Đang tải...</div>;
return (
<div className="min-h-screen bg-gray-100 flex items-center justify-center p-4">
<div className="max-w-md w-full bg-white rounded-lg shadow-md p-6">
<h1 className="text-2xl font-bold text-center mb-6 text-gray-800">
Kiểm thử Better Auth API
</h1>
{session?.user ? (
<div className="space-y-4">
<div className="bg-green-50 p-4 rounded-md">
<p className="text-green-800 font-semibold">Đã đăng nhập với tư cách:</p>
<p className="text-green-700">{session.user.email}</p>
<p className="text-green-600 text-sm">{session.user.name}</p>
</div>
<button
onClick={handleSignOut}
className="w-full bg-red-500 hover:bg-red-600 text-white font-medium py-2 px-4 rounded-md transition"
>
Đăng xuất
</button>
</div>
) : (
<div className="space-y-4">
<form onSubmit={handleSignUp} className="space-y-3">
<h2 className="text-lg font-semibold text-gray-700">Đăng ký</h2>
<input
type="text"
placeholder="Tên"
value={name}
onChange={(e) => setName(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
<input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
<input
type="password"
placeholder="Mật khẩu"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
<button
type="submit"
className="w-full bg-green-500 hover:bg-green-600 text-white font-medium py-2 px-4 rounded-md transition"
>
Tạo tài khoản
</button>
</form>
<form onSubmit={handleSignIn} className="space-y-3">
<h2 className="text-lg font-semibold text-gray-700">Đăng nhập</h2>
<input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
<input
type="password"
placeholder="Mật khẩu"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
<button
type="submit"
className="w-full bg-blue-500 hover:bg-blue-600 text-white font-medium py-2 px-4 rounded-md transition"
>
Đăng nhập
</button>
</form>
</div>
)}
</div>
</div>
);
}
export default App;

Bước 3: Kiểm thử tích hợp
1. Khởi động Máy chủ Backend
cd better-auth-backend
bun dev

2. Khởi động Phát triển Frontend
cd better-auth-frontend
bun dev

3. Kiểm thử luồng xác thực
- Mở
http://localhost:5173trong trình duyệt của bạn - Đăng ký người dùng mới bằng biểu mẫu Đăng ký


- Quan sát tệp cơ sở dữ liệu tăng kích thước khi các bảng được điền dữ liệu
- Đăng xuất và đăng nhập lại để xác minh quản lý phiên
- Kiểm tra điểm cuối phiên:
http://localhost:3000/api/auth/session

Lợi ích chính của Better Auth API
Better Auth API nổi bật nhờ một số lợi thế hấp dẫn:
- Không phụ thuộc Framework: Không giống như NextAuth.js hay Firebase Auth, Better Auth hoạt động ở bất cứ đâu JavaScript chạy. Logic xác thực tương tự phục vụ các client web, di động và API mà không cần sửa đổi.
- Nhiều chiến lược xác thực: Hỗ trợ sẵn có cho thông tin đăng nhập, OAuth 2.0, passkey, xác thực hai yếu tố và magic link. Mỗi chiến lược là một plugin có thể được kích hoạt chỉ với một dòng cấu hình.
- An toàn kiểu dữ liệu: Hỗ trợ TypeScript đầy đủ với các kiểu dữ liệu được suy luận từ schema cơ sở dữ liệu của bạn, loại bỏ tình trạng "any" type hell phổ biến trong các codebase xác thực.
- Tính linh hoạt của cơ sở dữ liệu: Bộ điều hợp Drizzle ORM có nghĩa là bạn có thể chuyển đổi giữa SQLite, PostgreSQL và MySQL mà không cần thay đổi logic xác thực của mình. Hướng dẫn này sử dụng SQLite để đơn giản, nhưng cùng một mã có thể mở rộng cho các cơ sở dữ liệu sản xuất.
- Hệ sinh thái Plugin: Cần hỗ trợ tổ chức? Đa người thuê (Multi-tenancy)? Vai trò quản trị viên? Hệ thống plugin của Better Auth cho phép bạn mở rộng chức năng mà không làm phình to lõi.
- Hiệu suất: Với Bun làm môi trường chạy, thời gian khởi động nguội dưới 100ms, và toàn bộ luồng xác thực hoàn thành trong vòng chưa đầy 50ms trên phần cứng khiêm tốn.
Các câu hỏi thường gặp
Q1: Tôi có thể sử dụng Better Auth API với npm thay vì Bun không?
Trả lời: Hoàn toàn có thể. Mặc dù hướng dẫn này sử dụng Bun vì lợi ích hiệu suất của nó, nhưng mọi lệnh đều có một lệnh tương đương trong npm. Thay thế bun add bằng npm install, bun dev bằng npm run dev, và bunx bằng npx. Mã duy nhất dành riêng cho Bun là import bun:sqlite, có thể được thay thế bằng better-sqlite3 cho môi trường Node.js.
Q2: Tại sao chúng ta cần Drizzle ORM? Better Auth không thể tự động tạo bảng sao?
Trả lời: Better Auth tuân theo nguyên tắc quản lý cơ sở dữ liệu rõ ràng. Drizzle cung cấp các migration an toàn kiểu dữ liệu, phiên bản schema và ngăn ngừa mất dữ liệu ngẫu nhiên. Lệnh drizzle-kit push là một thiết lập một lần giúp bạn kiểm soát hoàn toàn sự phát triển cơ sở dữ liệu của mình.
Q3: Điều gì sẽ xảy ra nếu tôi gặp lỗi "Missing parameter name"?
Trả lời: Lỗi này xảy ra khi sử dụng app.all() với ký tự đại diện trong Express. Giải pháp là sử dụng app.use("/api/auth", toNodeHandler(auth)) thay thế. Trình xử lý của Better Auth quản lý tất cả các sub-route nội bộ, vì vậy Express không cần khớp ký tự đại diện.
Q4: Làm cách nào để thêm các nhà cung cấp xác thực xã hội?
Trả lời: Kích hoạt các plugin OAuth trong cấu hình Better Auth của bạn. Ví dụ, để thêm GitHub:
import { betterAuth } from "better-auth";
import { github } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
github({
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
})
]
});
Q5: Better Auth API đã sẵn sàng cho sản xuất chưa?
Trả lời: Có. Better Auth cung cấp xác thực cho một số sản phẩm SaaS với hàng nghìn người dùng. Framework này triển khai quản lý phiên an toàn, bảo vệ CSRF và tuân thủ các hướng dẫn của OWASP. Tuy nhiên, hãy luôn kiểm tra triển khai cụ thể của bạn và giữ các phụ thuộc được cập nhật.
Kết luận
Việc xây dựng xác thực từ đầu không còn cần thiết với các giải pháp hiện đại như Better Auth API. Trong hướng dẫn này, chúng ta đã tạo một hệ thống xác thực hoàn chỉnh—từ schema cơ sở dữ liệu đến các thành phần UI—chỉ trong vài phút! Sự kết hợp giữa tính linh hoạt của Better Auth, tính an toàn kiểu dữ liệu của Drizzle ORM và hiệu suất của Bun tạo ra một trải nghiệm nhà phát triển có thể mở rộng từ nguyên mẫu đến sản phẩm thực tế.
Quá trình từng bước chứng minh rằng xác thực, mặc dù rất quan trọng, không cần phải phức tạp. Bằng cách tận dụng kiến trúc plugin và thiết kế không phụ thuộc framework của Better Auth, bạn có thể tập trung vào việc xây dựng các tính năng quan trọng đối với người dùng của mình thay vì vật lộn với các triển khai bảo mật.
Dù bạn đang xây dựng một dự án cá nhân hay ứng dụng doanh nghiệp, Better Auth API đều cung cấp nền tảng cho xác thực an toàn, có khả năng mở rộng, thích ứng với nhu cầu của bạn—chứ không phải ngược lại.
Bạn muốn một nền tảng tích hợp, tất cả trong một để Đội ngũ Phát triển của bạn làm việc cùng nhau với năng suất tối đa?
Apidog đáp ứng mọi yêu cầu của bạn, và thay thế Postman với mức giá phải chăng hơn nhiều!
