Better Auth
Better Auth is framework-agnostic authentication (and authorization) framework for TypeScript.
It provides a comprehensive set of features out of the box and includes a plugin ecosystem that simplifies adding advanced functionalities.
We recommended going through Better Auth basic setup before going through this page.
Handler
After setting up Better Auth instance, we can mount to Elysia via mount.
We need to mount the handler to Elysia endpoint.
import { Elysia } from 'elysia'
import { auth } from './auth'
const app = new Elysia().mount(auth.handler).listen(3000)
console.log(
`🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
)
Then we can access Better Auth with http://localhost:3000/api/auth
.
Custom endpoint
We recommended setting a prefix path for when using mount.
import { Elysia } from 'elysia'
import { auth } from './auth'
const app = new Elysia().mount('/auth', auth.handler).listen(3000) // ![code ++]
console.log(
`🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
)
Then we can access Better Auth with http://localhost:3000/auth/api/auth
.
But the URL looks redundant, we can customize the /api/auth
prefix to something else in Better Auth instance.
import { betterAuth } from 'better-auth'
import { openAPI } from 'better-auth/plugins'
import { passkey } from 'better-auth/plugins/passkey'
import { Pool } from 'pg'
export const auth = betterAuth({
basePath: '/api'
})
Then we can access Better Auth with http://localhost:3000/auth/api
.
Unfortunately, we can't set basePath
of a Better Auth instance to be empty or /
.
Swagger / OpenAPI
Better Auth support openapi
with better-auth/plugins
.
However if we are using @elysiajs/swagger, you might want to extract the documentation from Better Auth instance.
We may do that with the following code:
import { openAPI } from 'better-auth/plugins'
let _schema: ReturnType<typeof auth.api.generateOpenAPISchema>
const getSchema = async () => (_schema ??= auth.api.generateOpenAPISchema())
export const OpenAPI = {
getPaths: (prefix = '/auth/api') =>
getSchema().then(({ paths }) => {
const reference: typeof paths = Object.create(null)
for (const path of Object.keys(paths)) {
const key = prefix + path
reference[key] = paths[path]
for (const method of Object.keys(paths[path])) {
const operation = (reference[key] as any)[method]
operation.tags = ['Better Auth']
}
}
return reference
}) as Promise<any>,
components: getSchema().then(({ components }) => components) as Promise<any>
} as const
Then in our Elysia instance that use @elysiajs/swagger
.
import { Elysia } from 'elysia'
import { swagger } from '@elysiajs/swagger'
import { OpenAPI } from './auth'
const app = new Elysia().use(
swagger({
documentation: {
components: await OpenAPI.components,
paths: await OpenAPI.getPaths()
}
})
)
CORS
To configure cors, you can use the cors
plugin from @elysiajs/cors
.
import { Elysia } from 'elysia'
import { cors } from '@elysiajs/cors'
import { auth } from './auth'
const app = new Elysia()
.use(
cors({
origin: 'http://localhost:3001',
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
credentials: true,
allowedHeaders: ['Content-Type', 'Authorization']
})
)
.mount(auth.handler)
.listen(3000)
console.log(
`🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
)
Macro
You can use macro with resolve to provide session and user information before pass to view.
import { Elysia } from 'elysia'
import { auth } from './auth'
// user middleware (compute user and session and pass to routes)
const betterAuth = new Elysia({ name: 'better-auth' })
.mount(auth.handler)
.macro({
auth: {
async resolve({ status, request: { headers } }) {
const session = await auth.api.getSession({
headers
})
if (!session) return status(401)
return {
user: session.user,
session: session.session
}
}
}
})
const app = new Elysia()
.use(betterAuth)
.get('/user', ({ user }) => user, {
auth: true
})
.listen(3000)
console.log(
`🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
)
This will allow you to access the user
and session
object in all of your routes.