Cómo Usar la API de Better Auth para Autenticación Moderna

Ashley Goolam

Ashley Goolam

14 November 2025

Cómo Usar la API de Better Auth para Autenticación Moderna

La autenticación es la piedra angular de cualquier aplicación web moderna, sin embargo, configurarla sigue siendo uno de los desafíos que más tiempo consume para los desarrolladores. Presentamos Better Auth API—una solución de autenticación agnóstica al framework que promete transformar la forma en que implementamos la gestión de usuarios. En esta guía completa, recorreremos la construcción de una aplicación full-stack completa que demuestra el poder y la simplicidad de Better Auth, utilizando Bun para un rendimiento ultrarrápido.

💡
¿Quieres una excelente herramienta de prueba de API que genere hermosa documentación de API?

¿Quieres una plataforma integrada y todo en uno para que tu equipo de desarrolladores trabaje en conjunto con máxima productividad?

¡Apidog satisface todas tus demandas y reemplaza a Postman a un precio mucho más asequible!
botón

¿Qué es Better Auth API?

Better Auth API es un framework de autenticación moderno y de código abierto diseñado para funcionar sin problemas en cualquier entorno JavaScript. A diferencia de las bibliotecas de autenticación tradicionales que te encierran en ecosistemas específicos, Better Auth proporciona una API unificada que se adapta a tu pila, ya sea que uses React, Vue, Svelte o JavaScript puro en el frontend, y Node.js, Bun o Deno en el backend. Su arquitectura basada en plugins admite múltiples estrategias de autenticación, desde correo electrónico/contraseña tradicional hasta proveedores OAuth, passkeys y enlaces mágicos, todo ello manteniendo la seguridad de tipos y una excelente experiencia de desarrollador.

better auth

Primeros pasos con Better Auth: Requisitos previos y configuración del proyecto

Antes de sumergirnos en el código, asegúrate de tener lo siguiente instalado:

vs code ide

Si bien esta configuración funciona perfectamente con npm, demostraremos el flujo de trabajo de Bun, que ofrece una instalación de paquetes 3-5 veces más rápida y una experiencia de desarrollo más optimizada.

Construyendo un proyecto de ejemplo: Implementación paso a paso

Creemos un sistema de autenticación práctico con un frontend de React y un backend de Express, completo con persistencia en la base de datos.

Paso 1: Configuración del Backend con Express y Drizzle ORM

1. Inicializar el Proyecto Backend

Primero, crea y entra en tu directorio backend:

mkdir better-auth-backend
cd better-auth-backend
bun init -y

2. Instalar Dependencias

Necesitaremos Express para el servidor, Better Auth para la autenticación y Drizzle ORM para la gestión de la base de datos:

bun add express better-auth drizzle-orm
bun add -D @types/bun @types/express drizzle-kit

3. Configurar Variables de Entorno

Crea un archivo .env para almacenar la configuración sensible:

BETTER_AUTH_SECRET=your-secret-key-here # Generar con: openssl rand -base64 32
BETTER_AUTH_URL=http://localhost:3000
DATABASE_URL=local.db

El BETTER_AUTH_SECRET también se puede generar en el sitio web de better-auth.

4. Crear Esquema de Base de Datos con Drizzle ORM

Better Auth funciona mejor con Drizzle, que proporciona un excelente soporte de TypeScript y evita problemas de módulos nativos. Crea 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. Configurar Conexión a la Base de Datos

Crea src/db/index.ts usando el binding nativo de SQLite de 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. Configurar Better Auth

Crea src/lib/auth.ts para configurar la API de Better Auth:

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. Crear Servidor Express

En src/index.ts, monta el manejador de la API de Better Auth:

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,
  })
);

// Monta la API de Better Auth en /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. Ejecutar Migración de Base de Datos

Crea drizzle.config.ts en la raíz del backend:

import { defineConfig } from "drizzle-kit";

export default defineConfig({
  dialect: "sqlite",
  schema: "./src/db/schema.ts",
  out: "./drizzle",
  dbCredentials: {
    url: "local.db",
  },
});

Ejecuta la migración para crear las tablas:

bunx drizzle-kit push
estructura del proyecto del servidor backend

Paso 2: Configuración del Frontend con React y Vite

1. Crear Aplicación React

En una nueva terminal, inicializa el frontend:

bun create vite better-auth-frontend --template react-ts
cd better-auth-frontend

2. Instalar Dependencias

bun add better-auth

3. Configurar Tailwind CSS (Actualización V4)

Post-Tailwind CSS v4 requiere una configuración diferente. Instala los nuevos paquetes:

bun add -D tailwindcss postcss @tailwindcss/postcss

Crea tailwind.config.js en la raíz del proyecto:

/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

Crea postcss.config.js:

export default {
  plugins: {
    "@tailwindcss/postcss": {},
  },
}

Crea src/index.css:

@import "tailwindcss";

body {
  margin: 0;
  font-family: system-ui, -apple-system, sans-serif;
}

4. Configurar Cliente Better Auth

Crea 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. Construir la Interfaz de Usuario de Autenticación

Reemplaza src/App.tsx con una interfaz de autenticación completa:

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">Cargando...</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">
          Prueba de 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">Sesión iniciada como:</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"
            >
              Cerrar Sesión
            </button>
          </div>
        ) : (
          <div className="space-y-4">
            <form onSubmit={handleSignUp} className="space-y-3">
              <h2 className="text-lg font-semibold text-gray-700">Registrarse</h2>
              <input
                type="text"
                placeholder="Nombre"
                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="Correo electrónico"
                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="Contraseña"
                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"
              >
                Crear Cuenta
              </button>
            </form>

            <form onSubmit={handleSignIn} className="space-y-3">
              <h2 className="text-lg font-semibold text-gray-700">Iniciar Sesión</h2>
              <input
                type="email"
                placeholder="Correo electrónico"
                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="Contraseña"
                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"
              >
                Iniciar Sesión
              </button>
            </form>
          </div>
        )}
      </div>
    </div>
  );
}

export default App;
estructura del proyecto de la aplicación frontend

Paso 3: Probando la Integración

1. Iniciar Servidor Backend

cd better-auth-backend
bun dev
ejecutar el servidor backend de better auth

2. Iniciar Desarrollo Frontend

cd better-auth-frontend
bun dev
ejecutar la aplicación frontend de better auth

3. Probar el Flujo de Autenticación

registrarse o iniciar sesión
Información Correcta
intento de inicio de sesión con información incorrecta
Información Incorrecta
cerrar sesión

Beneficios clave de Better Auth API

La Better Auth API se distingue por varias ventajas convincentes:

Preguntas Frecuentes

P1: ¿Puedo usar Better Auth API con npm en lugar de Bun?
R: Absolutamente. Si bien esta guía utiliza Bun por sus beneficios de rendimiento, cada comando tiene un equivalente en npm. Reemplaza bun add por npm install, bun dev por npm run dev y bunx por npx. El único código específico de Bun es la importación bun:sqlite, que se puede reemplazar por better-sqlite3 para entornos Node.js.

P2: ¿Por qué necesitamos Drizzle ORM? ¿Better Auth no puede crear tablas automáticamente?
R: Better Auth sigue el principio de gestión explícita de la base de datos. Drizzle proporciona migraciones seguras de tipos, versionado de esquemas y previene la pérdida accidental de datos. El comando drizzle-kit push es una configuración única que te da control total sobre la evolución de tu base de datos.

P3: ¿Qué hago si encuentro el error "Missing parameter name"?
R: Esto ocurre al usar app.all() con comodines en Express. La solución es usar app.use("/api/auth", toNodeHandler(auth)) en su lugar. El manejador de Better Auth gestiona todas las subrutas internamente, por lo que Express no necesita la coincidencia de comodines.

P4: ¿Cómo agrego proveedores de autenticación social?
R: Habilita los plugins OAuth en tu configuración de Better Auth. Por ejemplo, para agregar 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,
    })
  ]
});

P5: ¿Better Auth API está lista para producción?
R: Sí. Better Auth impulsa la autenticación para varios productos SaaS con miles de usuarios. El framework implementa gestión segura de sesiones, protección CSRF y sigue las directrices de OWASP. Sin embargo, siempre audita tu implementación específica y mantén las dependencias actualizadas.

Conclusión

Construir la autenticación desde cero ya no es necesario con soluciones modernas como la Better Auth API. ¡En esta guía, hemos creado un sistema de autenticación completo, desde el esquema de la base de datos hasta los componentes de la interfaz de usuario, en pocos minutos! La combinación de la flexibilidad de Better Auth, la seguridad de tipos de Drizzle ORM y el rendimiento de Bun crea una experiencia de desarrollador que escala desde el prototipo hasta la producción.

El proceso paso a paso demuestra que la autenticación, aunque crítica, no tiene por qué ser compleja. Al aprovechar la arquitectura de plugins de Better Auth y su diseño agnóstico al framework, puedes concentrarte en construir características que importan a tus usuarios en lugar de luchar con las implementaciones de seguridad.

Ya sea que estés construyendo un proyecto personal o una aplicación empresarial, Better Auth API proporciona la base para una autenticación segura y escalable que se adapta a tus necesidades, y no al revés.

💡
¿Quieres una excelente herramienta de prueba de API que genere hermosa documentación de API?

¿Quieres una plataforma integrada y todo en uno para que tu equipo de desarrolladores trabaje en conjunto con máxima productividad?

¡Apidog satisface todas tus demandas y reemplaza a Postman a un precio mucho más asequible!
botón

Practica el diseño de API en Apidog

Descubre una forma más fácil de construir y usar APIs