Any example Hono + better-auth with other routes as example please #4660
|
Guys, I found an example where the JWT tokens is used, but it's not my case. I can't understand the documentation tried several approaches but it didn't work for me. Please |
Replies: 2 comments
|
Here's a working Hono + better-auth setup with protected routes: 1. Install dependenciesnpm install hono better-auth @better-auth/node2. Set up better-auth// src/auth.ts
import { betterAuth } from "better-auth";
export const auth = betterAuth({
database: {
provider: "sqlite", // or "pg", "mysql"
url: "./db.sqlite",
},
emailAndPassword: {
enabled: true,
},
});3. Mount better-auth handler in Hono// src/index.ts
import { Hono } from "hono";
import { cors } from "hono/cors";
import { auth } from "./auth";
const app = new Hono();
// CORS for frontend
app.use(
"/api/auth/**",
cors({
origin: "http://localhost:3001", // your frontend
allowHeaders: ["Content-Type", "Authorization"],
allowMethods: ["POST", "GET", "OPTIONS"],
credentials: true,
})
);
// Mount better-auth on /api/auth/*
app.on(["POST", "GET"], "/api/auth/**", (c) => {
return auth.handler(c.req.raw);
});
// --- Your other routes below ---
// Auth middleware for protected routes
const authMiddleware = async (c, next) => {
const session = await auth.api.getSession({
headers: c.req.raw.headers,
});
if (!session) {
return c.json({ error: "Unauthorized" }, 401);
}
c.set("user", session.user);
c.set("session", session.session);
return next();
};
// Public route
app.get("/api/health", (c) => c.json({ status: "ok" }));
// Protected route
app.get("/api/me", authMiddleware, (c) => {
const user = c.get("user");
return c.json({ user });
});
// Protected group
const protectedRoutes = new Hono();
protectedRoutes.use(authMiddleware);
protectedRoutes.get("/dashboard", (c) => {
return c.json({ message: `Hello ${c.get("user").name}` });
});
protectedRoutes.get("/settings", (c) => {
return c.json({ email: c.get("user").email });
});
app.route("/api", protectedRoutes);
export default app;4. Frontend client// On your frontend
import { createAuthClient } from "better-auth/client";
const authClient = createAuthClient({
baseURL: "http://localhost:3000/api/auth",
});
// Sign up
await authClient.signUp.email({
email: "user@example.com",
password: "password123",
name: "Test User",
});
// Sign in
await authClient.signIn.email({
email: "user@example.com",
password: "password123",
});
// Fetch protected route (cookies are sent automatically)
const res = await fetch("http://localhost:3000/api/me", {
credentials: "include",
});The key points:
|
|
Yes — here's a minimal but production-shaped Hono + Better Auth setup that handles auth on Server setup — import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { betterAuth } from 'better-auth';
import { drizzleAdapter } from 'better-auth/adapters/drizzle';
import { bearer, organization } from 'better-auth/plugins';
import { db } from './db'; // drizzle instance
// 1. Configure Better Auth
export const auth = betterAuth({
database: drizzleAdapter(db, { provider: 'pg' }),
emailAndPassword: { enabled: true },
socialProviders: {
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
},
plugins: [bearer(), organization()],
});
// 2. Session middleware — attach user + session to context
type Env = {
Variables: {
user: typeof auth.$Infer.Session.user | null;
session: typeof auth.$Infer.Session.session | null;
};
};
const app = new Hono<Env>();
app.use('*', cors({
origin: process.env.WEB_URL!,
credentials: true,
allowHeaders: ['Content-Type', 'Authorization'],
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
}));
app.use('*', async (c, next) => {
const session = await auth.api.getSession({ headers: c.req.raw.headers });
c.set('user', session?.user ?? null);
c.set('session', session?.session ?? null);
await next();
});
// 3. Mount Better Auth handler on /api/auth/**
app.on(['GET', 'POST'], '/api/auth/**', (c) => auth.handler(c.req.raw));
// 4. Public route
app.get('/', (c) => c.text('ok'));
// 5. Protected route
const requireUser = (): Hono<Env>['fetch'] => async (c, next) => {
if (!c.get('user')) return c.json({ error: 'unauthorized' }, 401);
await next();
};
app.get('/api/me', requireUser(), (c) => {
const user = c.get('user');
return c.json({ user });
});
app.get('/api/orgs', requireUser(), async (c) => {
const user = c.get('user')!;
const orgs = await auth.api.listOrganizations({
headers: c.req.raw.headers,
});
return c.json({ orgs, userId: user.id });
});
export default app;Client (browser) — import { createAuthClient } from 'better-auth/client';
import { organizationClient } from 'better-auth/client/plugins';
export const authClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_API_URL,
plugins: [organizationClient()],
});
// usage:
// await authClient.signIn.social({ provider: 'github' });
// const { data: session } = await authClient.useSession();
// await authClient.organization.create({ name: 'Acme' });Why the mount uses Better Auth's Runtime notes
Session type inference
A working template with organizations, invitations, and a Drizzle Postgres schema is at |
Here's a working Hono + better-auth setup with protected routes:
1. Install dependencies
2. Set up better-auth
3. Mount better-auth handler in Hono