Installation
Install better-iam, set the secret and base URL, create the betterIam() instance, connect PostgreSQL, SQLite, or libSQL, migrate, mount the HTTP handler in your framework, and create the typed client.
This page adds Better IAM to an existing application: one package, two environment variables, a configuration
module, a database, the HTTP handler mounted in your framework, and the typed browser client. When you finish, the
API answers under /api/iam and your server code can call iam.api and iam.require. The
Quickstart then signs in the first administrator and makes the first authorization check.
Requirements
| Requirement | Notes |
|---|---|
| Node.js 22.12 or newer | The engines field of every package. Web-standard Request/Response, Web Crypto, and AsyncLocalStorage are used throughout. |
| ESM | Packages are ES modules with TypeScript declarations. Use "type": "module" or a bundler. |
| Native modules | argon2 (password hashing) and, for SQLite, better-sqlite3. Allow their install scripts (pnpm: allowBuilds / onlyBuiltDependencies). |
| A database | PostgreSQL, SQLite, or libSQL/Turso through the bundled adapters, or your own adapter. |
| TypeScript (optional) | moduleResolution of bundler, node16, or nodenext so subpath exports resolve. |
Setup
Install the package
npm i better-iambetter-iam depends on every @better-iam/* package except the Nuxt module and re-exports each one as a subpath
(better-iam/adapter-postgres, better-iam/client, better-iam/next, and so on). Framework and protocol code
loads only when you import its subpath, so unused integrations cost nothing at runtime. It also installs the
better-iam command-line tool.
Framework peers (next, react, vue, svelte, @sveltejs/kit, react-router, @nestjs/*) are optional peer
dependencies: install the ones your application already uses. Nuxt projects also install @better-iam/nuxt.
Set environment variables
Better IAM derives its signing and encryption keys from one deployment secret of at least 32 characters. Generate
one with the CLI and append it to your .env file:
npx better-iam secret --env >> .envThen add the URL your application is served from:
BETTER_IAM_SECRET=generated-value
BETTER_IAM_BASE_URL=http://localhost:3000| Variable | Used for |
|---|---|
BETTER_IAM_SECRET | The secret option. Keep it stable across restarts and processes, and store it like a database password. Rotate it later with previousSecrets; see Secrets. |
BETTER_IAM_BASE_URL | The baseURL option: the public origin of your application. It becomes the trusted origin for cookie requests. |
Better IAM does not load .env files itself. Make the variables available to every process that loads the
configuration (your server, scripts, and the CLI), for example with your framework's .env support or
node --env-file=.env.
Create the instance
Put the options in a configuration module whose default export is the options object. Your application and the
better-iam CLI both load it, so migrations and scheduled jobs always see the same settings.
export default {
secret: process.env.BETTER_IAM_SECRET,
baseURL: process.env.BETTER_IAM_BASE_URL,
// database: added in the next step
};Then create the one instance the rest of your server code imports:
import { betterIam } from 'better-iam';
import config from './better-iam.config.mjs';
export const iam = betterIam(config);Frameworks expect this file in a server-only place: lib/iam.ts in Next.js, server/iam.ts in Nuxt,
src/lib/server/iam.ts in SvelteKit, and app/iam.server.ts in React Router.
Configure the database
Better IAM keeps all of its records in your database through a storage adapter, passed as the database option.
The three bundled adapters share one schema, so you can start on SQLite and move to PostgreSQL later.
import { postgresAdapter } from 'better-iam/adapter-postgres';
export default {
database: postgresAdapter({ connectionString: process.env.DATABASE_URL }),
secret: process.env.BETTER_IAM_SECRET,
baseURL: process.env.BETTER_IAM_BASE_URL,
};The recommended production store, through pg and kysely (both installed with the adapter). Optional
poolSize and lockTimeoutMs tune the connection pool and lock waits. See PostgreSQL.
See Storage operations for durability settings, snapshots, and moving between databases, and Adapters and plugins to write your own adapter.
Create the tables
Run the migrations once per deployment, before the application serves traffic:
npx better-iam migrateThe CLI finds the nearest better-iam.config.mjs (or .js, .ts); pass --config PATH or set
BETTER_IAM_CONFIG to use another file. migrate creates or upgrades the schema and applies plugin migrations, and
it is safe to run again.
To migrate from code instead, for example when a single-process app starts, call iam.initialize() before the
first request:
import { iam } from './iam';
await iam.initialize(); // creates or upgrades the schema, then runs plugin migrationsSee Migrations and the migrate command.
Mount the handler
The instance exposes a Web-standard iam.handler (Request to Response) and a Node iam.nodeHandler. The
browser client, passkey ceremonies, and the OAuth, SAML, and SCIM protocol mounts all reach the server through it,
under the basePath option (default /api/iam). Every API method is a POST; GET serves the operational
/health, /metrics (when a metrics token is configured), and /.well-known/jwks.json (when session JWTs are
enabled) endpoints.
import { createIamNext } from 'better-iam/next';
import { iam } from './iam';
export const iamNext = createIamNext(iam, { loginPath: '/login' });import { iamNext } from '@/lib/iam-next';
export const runtime = 'nodejs';
export const { GET, POST, OPTIONS } = iamNext.handlers();List the server packages and native modules in serverExternalPackages in next.config.mjs so they load as
plain Node modules. See Next.js.
Create the client
The typed client calls the mounted handler from the browser. Import the server instance with import type, so no
server code reaches the bundle; every method's input and result is then inferred from your configuration.
import { createIamClient } from 'better-iam/client';
import type { iam } from './iam';
export const client = createIamClient<typeof iam>();In a browser the client defaults to the page's origin and the /api/iam base path. Pass baseURL when the API
is on another origin or when the client runs outside a browser, and basePath when you changed the server's.
React, Vue, and Svelte bindings build on the same client; see Typed client.
Done
The API is live. Check that the configuration loads and the database is ready:
npx better-iam doctordoctor reports schema, bootstrap, secret strength, durability, email transport, and scheduled-job problems, and
--strict exits non-zero on any warning so you can run it in CI. See the doctor command.
A fresh database has no tenants yet. Continue with the Quickstart to bootstrap the root administrator, create an organization, and make your first authorization check.
Subpath imports
Every subpath of the umbrella package maps to one workspace package, so importing from a subpath loads only that package:
| Import | Package | What it provides |
|---|---|---|
better-iam | @better-iam/server and @better-iam/core | The common entry points: betterIam(), IamError, definePolicy, evaluatePolicy, verifyAssertion, verifyWebhookSignature, and the main types |
better-iam/server | @better-iam/server | Everything the server package exports, including defineConfig, the route tables, and server types |
better-iam/session-tokens | @better-iam/server/session-tokens | Offline verification of signed session JWTs in other services |
better-iam/core | @better-iam/core | Models, evaluatePolicy, definePolicy, the storage contract, IamError |
better-iam/auth, better-iam/auth/templates | @better-iam/auth | Authentication service internals and renderDeliveryMessage |
better-iam/client, /client/session, /client/passkeys, /client/device | @better-iam/client | Typed browser client, session store, WebAuthn helpers, registered-device proofs |
better-iam/adapter-postgres, /adapter-sqlite, /adapter-libsql | @better-iam/adapter-* | Storage adapters |
better-iam/oauth, better-iam/saml, better-iam/scim | @better-iam/oauth, saml, scim | Federation protocols |
better-iam/ldap | @better-iam/ldap | LDAP directory gateway |
better-iam/mcp, better-iam/a2a | @better-iam/mcp, a2a | Tool-level authorization for MCP servers; signed A2A agent cards and A2A server authorization |
better-iam/react, better-iam/vue | @better-iam/react, vue | UI bindings |
better-iam/next, /next/edge, /next/client | @better-iam/next | Next.js App Router helpers |
better-iam/svelte, /svelte/kit | @better-iam/svelte | Svelte stores and SvelteKit hooks |
better-iam/react-router | @better-iam/react-router | React Router middleware and guards |
better-iam/nestjs, /nestjs/testing | @better-iam/nestjs | NestJS module, guard, decorators |
better-iam/middleware, /express, /hono, /fastify | @better-iam/middleware | Node framework middleware |
better-iam/cli, better-iam/projects | @better-iam/cli, projects | CLI entry point, reference plugin |
The Nuxt module is published separately as @better-iam/nuxt because it depends on @nuxt/kit.
All packages
better-iam0.3.1Umbrella package: installs every package and exposes them as subpath imports.
/core/auth/auth/templates/server/session-tokens/oauth/saml/scim/adapter-postgres/adapter-sqlite/adapter-libsql/client/cli/client/passkeys/projects/react/next/next/edge/next/client/nestjs/nestjs/testing/vue/svelte/svelte/kit/middleware/express/hono/fastify/react-router/client/session/client/device/mcp/a2a/ldapPeers: @nestjs/common, @nestjs/core, next, react, vue, svelte, @sveltejs/kit, react-router
@better-iam/a2a0.3.1Agent2Agent (A2A) support: IAM-attested agent cards, card verification and discovery, and authorization for A2A servers.
@better-iam/adapter-libsql0.3.1libSQL / Turso storage adapter.
@better-iam/adapter-postgres0.3.1PostgreSQL storage adapter.
@better-iam/adapter-sqlite0.3.1SQLite storage adapter (better-sqlite3).
@better-iam/auth0.3.1Passwords, sessions, MFA, passkeys, magic links, recovery, and delivery templates.
/templates@better-iam/cli0.3.1The `better-iam` command line: migrations, bootstrap, audits, config as code, jobs.
@better-iam/client0.3.1Typed browser client with session store and passkey helpers.
/passkeys/session/device@better-iam/core0.3.1Models, the policy engine, the storage contract, the audit chain, and shared errors.
/conformance@better-iam/ldap0.3.1A read-only LDAPv3 directory gateway over Better IAM: people, groups and password binds for applications that only speak LDAP.
@better-iam/mcp0.3.1Tool-level authorization for Model Context Protocol servers: Better IAM credentials, agents and OAuth tokens.
@better-iam/middleware0.3.1Framework-neutral middleware core with Express, Hono, and Fastify adapters.
/express/hono/fastify@better-iam/nestjs0.3.1NestJS module, guard, decorators, and testing utilities.
/testingPeers: @nestjs/common, @nestjs/core, reflect-metadata, rxjs
@better-iam/next0.3.1Next.js App Router helpers: guarded pages, routes, actions, and edge checks.
/edge/clientPeers: next, react
@better-iam/nuxt0.3.1Nuxt module and h3 helpers.
/h3Peers: nuxt, vue
@better-iam/oauth0.3.1OAuth/OIDC authorization server, resource-server helpers, and Shared Signals.
@better-iam/projects0.3.1Reference tenant-scoped Projects plugin.
@better-iam/react0.3.1React provider, hooks, and permission-gated components.
Peers: react
@better-iam/react-router0.3.1React Router (framework mode) middleware, guarded loaders, and actions.
Peers: react-router
@better-iam/saml0.3.1SAML 2.0 service provider with tenant-managed connections.
@better-iam/scim0.3.1SCIM 2.0 inbound provisioning and outbound provisioning to applications.
@better-iam/server0.3.1The `betterIam()` factory: tenants, identities, authorization, governance, HTTP API.
/assertions/session-tokens@better-iam/svelte0.3.1Svelte stores and SvelteKit hooks, guards, and actions.
/kitPeers: @sveltejs/kit, svelte
@better-iam/vue0.3.1Vue plugin, composables, and the `IamCan` component.
Peers: vue
Next steps
Bootstrap the root, create an organization, define a role, and make your first authorization check.
IntegrationsSessions, guards, and rendering by permission in your framework.
Architecture overviewHow tenants, identities, resources, and the request pipeline fit together.
Options referenceEvery option betterIam() accepts.
Better IAM is created by Sean Filimon
Last updated
Comparison
How Better IAM differs from hosted identity providers, authentication libraries, authorization services, and Keycloak, and when one of those is the better choice.
Quickstart
Bootstrap the root administrator, create an organization, define a role, enforce an authorization check, and use the basic sign-up, sign-in, session, and authorization calls from the server and the browser.