Remix Adapter
Adapter for integrating Better Auth Studio with Remix applications.
Import
import { betterAuthStudio } from 'better-auth-studio/remix';
betterAuthStudio()
Creates a Remix-compatible request handler for Better Auth Studio.
Function Signature
function betterAuthStudio(config: StudioConfig): ({ request }: { request: Request }) => Promise<Response>
Parameters
Studio configuration object containing authentication and feature settings.Show StudioConfig properties
Your Better Auth instance
Base path for the studio routes. Default: "/api/studio"
Access control configuration for the studio
Customization options for branding and theming
Last-seen tracking configuration
IP geolocation provider configuration
Event tracking and ingestion configuration
Tools configuration with exclude list
Returns
An async function that accepts a Remix request context and returns a Response
Usage
Resource Route Handler
Create a resource route with a catch-all parameter:
// app/routes/api.studio.$.ts
import { betterAuthStudio } from 'better-auth-studio/remix';
import { auth } from '~/lib/auth.server';
import { defineStudioConfig } from 'better-auth-studio';
import type { LoaderFunctionArgs, ActionFunctionArgs } from '@remix-run/node';
const studioConfig = defineStudioConfig({
auth,
basePath: '/api/studio',
metadata: {
title: 'My App Studio',
theme: 'dark'
},
events: {
enabled: true,
tableName: 'auth_events'
}
});
const handler = betterAuthStudio(studioConfig);
export async function loader({ request }: LoaderFunctionArgs) {
return handler({ request });
}
export async function action({ request }: ActionFunctionArgs) {
return handler({ request });
}
Simplified Version
// app/routes/api.studio.$.ts
import { betterAuthStudio } from 'better-auth-studio/remix';
import { auth } from '~/lib/auth.server';
const handler = betterAuthStudio({ auth });
export const loader = handler;
export const action = handler;
With Access Control
// app/routes/api.studio.$.ts
import { betterAuthStudio } from 'better-auth-studio/remix';
import { auth } from '~/lib/auth.server';
import type { LoaderFunctionArgs, ActionFunctionArgs } from '@remix-run/node';
const handler = betterAuthStudio({
auth,
access: {
roles: ['admin'],
allowEmails: ['[email protected]'],
sessionDuration: 60 * 60 * 24 // 24 hours
}
});
export async function loader({ request }: LoaderFunctionArgs) {
return handler({ request });
}
export async function action({ request }: ActionFunctionArgs) {
return handler({ request });
}
With Custom Middleware
// app/routes/api.studio.$.ts
import { betterAuthStudio } from 'better-auth-studio/remix';
import { auth } from '~/lib/auth.server';
import type { LoaderFunctionArgs, ActionFunctionArgs } from '@remix-run/node';
const studioHandler = betterAuthStudio({ auth });
// Custom middleware for logging
async function withLogging(
handler: typeof studioHandler,
args: LoaderFunctionArgs | ActionFunctionArgs
) {
console.log(`Studio request: ${args.request.method} ${args.request.url}`);
const response = await handler(args);
console.log(`Studio response: ${response.status}`);
return response;
}
export async function loader(args: LoaderFunctionArgs) {
return withLogging(studioHandler, args);
}
export async function action(args: ActionFunctionArgs) {
return withLogging(studioHandler, args);
}
Implementation Details
Request Conversion
The adapter converts Remix request objects to the universal format:
type UniversalRequest = {
url: string; // Pathname + search (with basePath stripped)
method: string; // request.method
headers: Record<string, string>; // request.headers
body?: any; // Parsed from request
};
Base Path Handling
The adapter automatically strips the base path from URLs:
// If basePath is '/api/studio'
// And request URL is '/api/studio/users'
// The universal URL will be '/users'
Content Type Handling
The adapter handles multiple content types:
application/json → request.json()
application/x-www-form-urlencoded → FormData converted to object
multipart/form-data → FormData converted to object
- Other → Attempts to parse as text, then JSON
Error Handling
Errors are caught and returned as JSON responses:
{
error: "Internal server error"
}
Status code: 500
Remix Route Naming
The route file naming follows Remix conventions:
api.studio.$.ts → /api/studio/*
api.studio.$splat.ts → /api/studio/* (alternative)
routes/api/studio/$.ts → /api/studio/* (nested routing)
File Structure
app/
├── routes/
│ └── api.studio.$.ts
└── lib/
└── auth.server.ts