Otentikasi adalah landasan setiap aplikasi web modern, namun pengaturannya tetap menjadi salah satu tantangan paling memakan waktu yang dihadapi pengembang. Hadir Better Auth API—solusi otentikasi agnostik-framework yang menjanjikan untuk mengubah cara kita mengimplementasikan manajemen pengguna. Dalam panduan komprehensif ini, kita akan membahas pembangunan aplikasi full-stack lengkap yang menunjukkan kekuatan dan kesederhanaan Better Auth, menggunakan Bun untuk kinerja yang sangat cepat.
Menginginkan platform All-in-One terintegrasi untuk Tim Pengembang Anda agar dapat bekerja sama dengan produktivitas maksimum?
Apidog memenuhi semua permintaan Anda, dan menggantikan Postman dengan harga yang jauh lebih terjangkau!
Apa itu Better Auth API?
Better Auth API adalah kerangka kerja otentikasi modern, sumber terbuka, yang dirancang untuk bekerja dengan mulus di setiap lingkungan JavaScript. Berbeda dengan pustaka otentikasi tradisional yang mengunci Anda ke ekosistem tertentu, Better Auth menyediakan API terpadu yang beradaptasi dengan tumpukan teknologi Anda—baik Anda menggunakan React, Vue, Svelte, atau vanilla JavaScript di frontend, dan Node.js, Bun, atau Deno di backend. Arsitektur berbasis plugin-nya mendukung berbagai strategi otentikasi, mulai dari email/kata sandi tradisional hingga penyedia OAuth, passkey, dan magic link, semuanya sambil mempertahankan keamanan tipe dan pengalaman pengembang yang sangat baik.

Memulai dengan Better Auth: Prasyarat dan Penyiapan Proyek
Sebelum menyelami kode, pastikan Anda telah menginstal hal-hal berikut:
- Bun 1.0+: Kami akan menggunakan Bun sepanjang tutorial ini karena kecepatan luar biasa dan perkakas modernnya.
- Node.js 18+: Diperlukan untuk runtime backend.
- Editor kode: VS Code direkomendasikan untuk dukungan TypeScript.

Meskipun penyiapan ini berfungsi sempurna dengan npm, kami akan mendemonstrasikan alur kerja Bun, yang menawarkan instalasi paket 3-5 kali lebih cepat dan pengalaman pengembangan yang lebih efisien.
Membangun Proyek Contoh: Implementasi Langkah demi Langkah
Mari kita buat sistem otentikasi praktis dengan frontend React dan backend Express, lengkap dengan persistensi basis data.
Langkah 1: Penyiapan Backend dengan Express dan Drizzle ORM
1. Inisialisasi Proyek Backend
Pertama, buat dan masuk ke direktori backend Anda:
mkdir better-auth-backend
cd better-auth-backend
bun init -y
2. Instal Dependensi
Kita akan membutuhkan Express untuk server, Better Auth untuk otentikasi, dan Drizzle ORM untuk manajemen basis data:
bun add express better-auth drizzle-orm
bun add -D @types/bun @types/express drizzle-kit
3. Konfigurasi Variabel Lingkungan
Buat file .env untuk menyimpan konfigurasi sensitif:
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 juga dapat dibuat di situs web better-auth.
4. Buat Skema Basis Data dengan Drizzle ORM
Better Auth bekerja paling baik dengan Drizzle, yang menyediakan dukungan TypeScript yang sangat baik dan menghindari masalah modul native. Buat 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. Konfigurasi Koneksi Basis Data
Buat src/db/index.ts menggunakan binding SQLite native 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. Siapkan Konfigurasi Better Auth
Buat src/lib/auth.ts untuk mengonfigurasi 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. Buat Server Express
Di src/index.ts, pasang handler 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,
})
);
// Mount Better Auth API at /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(`Server running on http://localhost:${PORT}`);
});
8. Jalankan Migrasi Basis Data
Buat drizzle.config.ts di root backend:
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "sqlite",
schema: "./src/db/schema.ts",
out: "./drizzle",
dbCredentials: {
url: "local.db",
},
});
Jalankan migrasi untuk membuat tabel:
bunx drizzle-kit push

Langkah 2: Penyiapan Frontend dengan React dan Vite
1. Buat Aplikasi React
Di terminal baru, inisialisasi frontend:
bun create vite better-auth-frontend --template react-ts
cd better-auth-frontend
2. Instal Dependensi
bun add better-auth
3. Konfigurasi Tailwind CSS (Pembaruan V4)
Pasca-Tailwind CSS v4 memerlukan penyiapan yang berbeda. Instal paket-paket baru:
bun add -D tailwindcss postcss @tailwindcss/postcss
Buat tailwind.config.js di root proyek:
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
Buat postcss.config.js:
export default {
plugins: {
"@tailwindcss/postcss": {},
},
}
Buat src/index.css:
@import "tailwindcss";
body {
margin: 0;
font-family: system-ui, -apple-system, sans-serif;
}
4. Siapkan Klien Better Auth
Buat 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. Bangun UI Otentikasi
Ganti src/App.tsx dengan antarmuka otentikasi lengkap:
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">Memuat...</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">
Uji 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">Masuk sebagai:</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"
>
Keluar
</button>
</div>
) : (
<div className="space-y-4">
<form onSubmit={handleSignUp} className="space-y-3">
<h2 className="text-lg font-semibold text-gray-700">Daftar</h2>
<input
type="text"
placeholder="Nama"
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="Kata Sandi"
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"
>
Buat Akun
</button>
</form>
<form onSubmit={handleSignIn} className="space-y-3">
<h2 className="text-lg font-semibold text-gray-700">Masuk</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="Kata Sandi"
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"
>
Masuk
</button>
</form>
</div>
)}
</div>
</div>
);
}
export default App;

Langkah 3: Menguji Integrasi
1. Mulai Server Backend
cd better-auth-backend
bun dev

2. Mulai Pengembangan Frontend
cd better-auth-frontend
bun dev

3. Uji Alur Otentikasi
- Buka
http://localhost:5173di browser Anda - Daftarkan pengguna baru menggunakan formulir Pendaftaran


- Amati file basis data yang bertambah seiring tabel terisi
- Keluar dan masuk kembali untuk memverifikasi manajemen sesi
- Periksa endpoint sesi:
http://localhost:3000/api/auth/session

Manfaat Utama Better Auth API
Better Auth API membedakan dirinya melalui beberapa keunggulan menarik:
- Agnostisisme Framework: Tidak seperti NextAuth.js atau Firebase Auth, Better Auth berfungsi di mana pun JavaScript berjalan. Logika otentikasi yang sama melayani klien web, seluler, dan API tanpa modifikasi.
- Berbagai Strategi Otentikasi: Dukungan langsung untuk kredensial, OAuth 2.0, passkey, otentikasi dua faktor, dan magic link. Setiap strategi adalah plugin yang dapat diaktifkan dengan satu baris konfigurasi.
- Keamanan Tipe (Type Safety): Dukungan TypeScript penuh dengan tipe yang disimpulkan dari skema basis data Anda menghilangkan "neraka" tipe "any" yang umum dalam basis kode otentikasi.
- Fleksibilitas Basis Data: Adaptor Drizzle ORM berarti Anda dapat beralih antara SQLite, PostgreSQL, dan MySQL tanpa mengubah logika otentikasi Anda. Tutorial ini menggunakan SQLite untuk kesederhanaan, tetapi kode yang sama dapat diskalakan ke basis data produksi.
- Ekosistem Plugin: Butuh dukungan organisasi? Multi-tenancy? Peran admin? Sistem plugin Better Auth memungkinkan Anda memperluas fungsionalitas tanpa membebani inti.
- Kinerja: Dengan Bun sebagai runtime, cold start berada di bawah 100ms, dan seluruh alur otentikasi selesai dalam waktu kurang dari 50ms pada perangkat keras sederhana.
Pertanyaan yang Sering Diajukan
Q1: Bisakah saya menggunakan Better Auth API dengan npm alih-alih Bun?
Jawab: Tentu saja. Meskipun panduan ini menggunakan Bun untuk keuntungan kinerjanya, setiap perintah memiliki padanan npm. Ganti bun add dengan npm install, bun dev dengan npm run dev, dan bunx dengan npx. Satu-satunya kode khusus Bun adalah impor bun:sqlite, yang dapat diganti dengan better-sqlite3 untuk lingkungan Node.js.
Q2: Mengapa kita membutuhkan Drizzle ORM? Tidakkah Better Auth bisa membuat tabel secara otomatis?
Jawab: Better Auth mengikuti prinsip manajemen basis data eksplisit. Drizzle menyediakan migrasi yang aman tipe, versi skema, dan mencegah kehilangan data yang tidak disengaja. Perintah drizzle-kit push adalah penyiapan satu kali yang memberi Anda kontrol penuh atas evolusi basis data Anda.
Q3: Bagaimana jika saya mengalami kesalahan "Missing parameter name"?
Jawab: Ini terjadi saat menggunakan app.all() dengan wildcard di Express. Solusinya adalah menggunakan app.use("/api/auth", toNodeHandler(auth)) sebagai gantinya. Handler Better Auth mengelola semua sub-rute secara internal, sehingga Express tidak memerlukan pencocokan wildcard.
Q4: Bagaimana cara menambahkan penyedia otentikasi sosial?
Jawab: Aktifkan plugin OAuth di konfigurasi Better Auth Anda. Misalnya, untuk menambahkan 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: Apakah Better Auth API siap produksi?
Jawab: Ya. Better Auth mendukung otentikasi untuk beberapa produk SaaS dengan ribuan pengguna. Kerangka kerja ini mengimplementasikan manajemen sesi yang aman, perlindungan CSRF, dan mengikuti panduan OWASP. Namun, selalu audit implementasi spesifik Anda dan jaga agar dependensi tetap diperbarui.
Kesimpulan
Membangun otentikasi dari awal tidak lagi diperlukan dengan solusi modern seperti Better Auth API. Dalam panduan ini, kami telah membuat sistem otentikasi lengkap—mulai dari skema basis data hingga komponen UI—dalam beberapa menit! Kombinasi fleksibilitas Better Auth, keamanan tipe Drizzle ORM, dan kinerja Bun menciptakan pengalaman pengembang yang dapat diskalakan dari prototipe hingga produksi.
Proses langkah demi langkah ini menunjukkan bahwa otentikasi, meskipun penting, tidak perlu rumit. Dengan memanfaatkan arsitektur plugin Better Auth dan desain agnostik-framework, Anda dapat fokus membangun fitur-fitur yang penting bagi pengguna Anda daripada bergulat dengan implementasi keamanan.
Baik Anda membangun proyek sampingan atau aplikasi perusahaan, Better Auth API menyediakan fondasi untuk otentikasi yang aman, terukur, dan beradaptasi dengan kebutuhan Anda—bukan sebaliknya.
Menginginkan platform All-in-One terintegrasi untuk Tim Pengembang Anda agar dapat bekerja sama dengan produktivitas maksimum?
Apidog memenuhi semua permintaan Anda, dan menggantikan Postman dengan harga yang jauh lebih terjangkau!
