Back to Better Auth

Optimizing for Performance

docs/content/docs/guides/optimizing-for-performance.mdx

1.6.197.6 KB
Original Source

In this guide, we’ll go over some of the ways you can optimize your application for a more performant Better Auth app.

Caching

Caching is a powerful technique that can significantly improve the performance of your Better Auth application by reducing the number of database queries and speeding up response times.

Calling your database every time useSession or getSession is invoked isn’t ideal, especially if sessions don’t change frequently. Cookie caching handles this by storing session data in a short-lived, signed cookie similar to how JWT access tokens are used with refresh tokens.

To turn on cookie caching, just set session.cookieCache in your auth config:

ts
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  session: {
    cookieCache: {
      enabled: true,
      maxAge: 5 * 60, // Cache duration in seconds
    },
  },
});

Read more about cookie caching.

Framework Caching

Here are examples of how you can do caching in different frameworks and environments:

<Tabs items={["Next", "react-router", "SolidStart", "TanStack Query"]}> <Tab value="Next"> Since Next v15, we can use the "use cache" directive to cache the response of a server function.

```ts
export async function getUsers() {
    'use cache' // [!code highlight]
    const { users } = await auth.api.listUsers();
    return users
}
```

Learn more about <Link href="https://nextjs.org/docs/app/api-reference/directives/use-cache">NextJS use cache directive</Link>.
</Tab> <Tab value="react-router"> In React Router v7, you use HTTP `Cache-Control` headers in the loader function to cache responses. You must also export a headers function to send these headers. Here's an example:
```ts
import { data } from 'react-router';
import type { Route } from './+types/your-route-name';

export const loader = async ({ request }: Route.LoaderArgs) => {
  const { users } = await auth.api.listUsers();
  
  return data(users, {
    headers: {
      'Cache-Control': 'max-age=3600', // Cache for 1 hour
    },
  });
};

export function headers({ loaderHeaders }: Route.HeadersArgs) {
  return loaderHeaders;
}
```
</Tab> <Tab value="SolidStart"> In SolidStart, you can use the `query` function to cache data. Here’s an example:
```tsx
const getUsers = query(
    async () => (await auth.api.listUsers()).users,
    "getUsers"
);
```

Learn more about SolidStart <Link href="https://docs.solidjs.com/solid-router/reference/data-apis/query">`query` function</Link>.
</Tab> <Tab value="TanStack Query"> With TanStack Query you can use the `useQuery` hook to cache data. Here’s an example:
```ts
import { useQuery } from '@tanstack/react-query';

const fetchUsers = async () => {
    const { users } = await auth.api.listUsers();
    return users;
};

export default function Users() {
    const { data: users, isLoading } = useQuery('users', fetchUsers, {
        staleTime: 1000 * 60 * 15, // Cache for 15 minutes
    });

    if (isLoading) return <div>Loading...</div>;

    return (
        <ul>
            {users.map(user => (
                <li key={user.id}>{user.name}</li>
            ))}
        </ul>
    );
}
```

Learn more about <Link href="https://tanstack.com/query">TanStack Query</Link>.
</Tab> </Tabs>

Background Tasks

On serverless platforms (Vercel, Cloudflare Workers), you can improve response times by deferring non-critical work—cleanup, analytics, rate limit updates, email sending—to run after the response is sent. Configure advanced.backgroundTasks and use ctx.context.runInBackground or ctx.context.runInBackgroundOrAwait in hooks.

ts
import { betterAuth } from "better-auth";
import { createAuthMiddleware } from "better-auth/api";
import { waitUntil } from "@vercel/functions";

export const auth = betterAuth({
  advanced: {
    backgroundTasks: { handler: waitUntil },
  },
  hooks: {
    after: createAuthMiddleware(async (ctx) => {
      if (ctx.path === "/sign-up/email") {
        ctx.context.runInBackground(logSignUp(ctx.context.newSession?.user.id));
      }
    }),
  },
});

See the Context documentation for more examples.

SSR Optimizations

If you're using a framework that supports server-side rendering, it's usually best to pre-fetch the user session on the server and use it as a fallback on the client.

ts
const session = await auth.api.getSession({
  headers: await headers(),
});
//then pass the session to the client

Database optimizations

Optimizing database performance is essential to get the best out of Better Auth.

Recommended fields to index

TableFieldsPlugin
usersemail
accountsuserId
sessionsuserId, token
verificationsidentifier
invitationsemail, organizationIdorganization
membersuserId, organizationIdorganization
organizationsslugorganization
passkeyuserIdpasskey
twoFactorsecrettwoFactor
<Callout> We intend to add indexing support in our schema generation tool in the future. </Callout>

Bundle Size Optimization

If you're using custom adapters (like Prisma, Drizzle, or MongoDB), you can reduce your bundle size by using better-auth/minimal instead of better-auth. This version excludes Kysely, which is only needed when using direct database connections.

Usage

Simply import from better-auth/minimal instead of better-auth:

<Tabs items={["Prisma", "Drizzle", "MongoDB"]}> <Tab value="Prisma"> ```ts title="auth.ts" import { betterAuth } from "better-auth/minimal"; // [!code highlight] import { prismaAdapter } from "better-auth/adapters/prisma"; import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

export const auth = betterAuth({
  database: prismaAdapter(prisma, {
    provider: "postgresql", // or "mysql", "sqlite"
  }),
});
```
</Tab> <Tab value="Drizzle"> ```ts title="auth.ts" import { betterAuth } from "better-auth/minimal"; // [!code highlight] import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { db } from "./database";
export const auth = betterAuth({
  database: drizzleAdapter(db, {
    provider: "pg", // or "mysql", "sqlite"
  }),
});
```
</Tab> <Tab value="MongoDB"> ```ts title="auth.ts" import { betterAuth } from "better-auth/minimal"; // [!code highlight] import { mongodbAdapter } from "better-auth/adapters/mongodb"; import { MongoClient } from "mongodb";
const client = new MongoClient(process.env.DATABASE_URL!);
const db = client.db();

export const auth = betterAuth({
  database: mongodbAdapter(db),
});
```
</Tab> </Tabs> <Callout type="warning"> **Limitations:**
  • Direct database connections are not supported (you must use an adapter)
  • Built-in migrations are not supported. Use external migration tools (or use better-auth if you need built-in migration support)
</Callout>