docs/content/docs/integrations/encore.mdx
Better Auth can be integrated with your Encore application (an open source TypeScript framework with automated infrastructure and observability).
Before you start, make sure you have a Better Auth instance configured. If you haven't done that yet, check out the installation.
Install the Encore CLI and create a new application. This will scaffold a TypeScript project with the required structure:
brew install encoredev/tap/encore # if you don't have Encore installed
encore app create my-app --example=ts/hello-world
cd my-app
npm install better-auth
To handle auth requests, mount Better Auth on a catch-all endpoint using Encore's api.raw():
import { api } from "encore.dev/api";
import { toNodeHandler } from "better-auth/node";
import { auth } from "./auth"; // Your Better Auth instance
export const authHandler = api.raw(
{ expose: true, path: "/api/auth/*path", method: "*" },
toNodeHandler(auth)
);
If your frontend runs on a different origin, configure CORS in your encore.app file to allow credentials (cookies) to be sent with requests:
{
"id": "your-app",
"global_cors": {
"allow_origins_with_credentials": ["http://localhost:3000"]
}
}
When requests come from a different origin, they are blocked by default. Add trusted origins to your Better Auth config:
export const auth = betterAuth({
trustedOrigins: ["http://localhost:3000", "https://your-app.com"],
// ... rest of config
});
Start your app with the Encore CLI. Make sure Docker is running as Encore uses it to manage local infrastructure:
encore run
Encore has a built-in auth handler pattern for protecting endpoints. Create an auth handler that validates Better Auth sessions:
import { APIError, Gateway, Header } from "encore.dev/api";
import { authHandler } from "encore.dev/auth";
import { auth } from "./auth";
interface AuthParams {
authorization: Header<"Authorization">;
cookie: Header<"Cookie">;
}
interface AuthData {
userID: string;
email: string;
name: string;
}
const handler = authHandler(async (params: AuthParams): Promise<AuthData> => {
const headers = new Headers();
if (params.authorization) {
headers.set("Authorization", params.authorization);
}
if (params.cookie) {
headers.set("Cookie", params.cookie);
}
const session = await auth.api.getSession({ headers });
if (!session?.user) {
throw APIError.unauthenticated("invalid session");
}
return {
userID: session.user.id,
email: session.user.email,
name: session.user.name,
};
});
export const gateway = new Gateway({ authHandler: handler });
Then protect any endpoint with auth: true:
import { api } from "encore.dev/api";
import { getAuthData } from "~encore/auth";
export const getProfile = api(
{ expose: true, auth: true, method: "GET", path: "/profile" },
async () => {
const authData = getAuthData()!;
return { id: authData.userID, email: authData.email };
}
);
For a complete walkthrough including database setup and deployment, see the Better Auth with Encore tutorial.