import { Callout } from "nextra/components"
import { Code } from "@/components/Code"

<img align="right" src="/img/adapters/typeorm.svg" height="64" width="64" />

# TypeORM Adapter

## Resources

- [TypeORM documentation](https://typeorm.io)

## Setup

### Installation

```bash npm2yarn
npm install @auth/typeorm-adapter typeorm
```

### Environment Variables

```sh
AUTH_TYPEORM_CONNECTION=postgres://postgres:adminadmin@0.0.0.0:5432/db
```

### Configuration

<Code>
<Code.Next>

```ts filename="./auth.ts"
import NextAuth from "next-auth"
import { TypeORMAdapter } from "@auth/typeorm-adapter"

export const { handlers, auth, signIn, signOut } = NextAuth({
  adapter: TypeORMAdapter(process.env.AUTH_TYPEORM_CONNECTION),
})
```

</Code.Next>
<Code.Qwik>

```ts filename="/src/routes/plugin@auth.ts"
import { QwikAuth$ } from "@auth/qwik"
import { TypeORMAdapter } from "@auth/typeorm-adapter"

export const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(
  () => ({
    providers: [],
    adapter: TypeORMAdapter(import.meta.env.AUTH_TYPEORM_CONNECTION),
  })
)
```

</Code.Qwik>
<Code.Svelte>

```ts filename="./src/auth.ts"
import { SvelteKitAuth } from "@auth/sveltekit"
import { TypeORMAdapter } from "@auth/typeorm-adapter"

export const { handle, signIn, signOut } = SvelteKitAuth({
  adapter: TypeORMAdapter(process.env.AUTH_TYPEORM_CONNECTION),
})
```

</Code.Svelte>
<Code.Express>

```ts filename="./src/routes/auth.route.ts"
import { ExpressAuth } from "@auth/express"
import { TypeORMAdapter } from "@auth/typeorm-adapter"

const app = express()

app.set("trust proxy", true)
app.use(
  "/auth/*",
  ExpressAuth({
    providers: [],
    adapter: TypeORMAdapter(process.env.AUTH_TYPEORM_CONNECTION),
  })
)
```

</Code.Express>
</Code>

`TypeORMAdapter` takes either a connection string, or a [`ConnectionOptions`](https://github.com/typeorm/typeorm/blob/master/docs/connection-options.md) object as its first parameter.

### Advanced usage

#### Custom models

The TypeORM adapter uses [`Entity` classes](https://github.com/typeorm/typeorm/blob/master/docs/entities.md) to define the shape of your data.

You can override the default entities and add additional fields with a custom entities file.

1. Create a file containing your modified entities:

```ts filename="lib/entities.ts" {38-39}
import {
  Entity,
  PrimaryGeneratedColumn,
  Column,
  ManyToOne,
  OneToMany,
  ValueTransformer,
} from "typeorm"

const transformer: Record<"date" | "bigint", ValueTransformer> = {
  date: {
    from: (date: string | null) => date && new Date(parseInt(date, 10)),
    to: (date?: Date) => date?.valueOf().toString(),
  },
  bigint: {
    from: (bigInt: string | null) => bigInt && parseInt(bigInt, 10),
    to: (bigInt?: number) => bigInt?.toString(),
  },
}

@Entity({ name: "users" })
export class UserEntity {
  @PrimaryGeneratedColumn("uuid")
  id!: string

  @Column({ type: "varchar", nullable: true })
  name!: string | null

  @Column({ type: "varchar", nullable: true, unique: true })
  email!: string | null

  @Column({ type: "varchar", nullable: true, transformer: transformer.date })
  emailVerified!: string | null

  @Column({ type: "varchar", nullable: true })
  image!: string | null

+ @Column({ type: "varchar", nullable: true })
+ role!: string | null

  @OneToMany(() => SessionEntity, (session) => session.userId)
  sessions!: SessionEntity[]

  @OneToMany(() => AccountEntity, (account) => account.userId)
  accounts!: AccountEntity[]
}

@Entity({ name: "accounts" })
export class AccountEntity {
  @PrimaryGeneratedColumn("uuid")
  id!: string

  @Column({ type: "uuid" })
  userId!: string

  @Column()
  type!: string

  @Column()
  provider!: string

  @Column()
  providerAccountId!: string

  @Column({ type: "varchar", nullable: true })
  refresh_token!: string | null

  @Column({ type: "varchar", nullable: true })
  access_token!: string | null

  @Column({
    nullable: true,
    type: "bigint",
    transformer: transformer.bigint,
  })
  expires_at!: number | null

  @Column({ type: "varchar", nullable: true })
  token_type!: string | null

  @Column({ type: "varchar", nullable: true })
  scope!: string | null

  @Column({ type: "varchar", nullable: true })
  id_token!: string | null

  @Column({ type: "varchar", nullable: true })
  session_state!: string | null

  @Column({ type: "varchar", nullable: true })
  oauth_token_secret!: string | null

  @Column({ type: "varchar", nullable: true })
  oauth_token!: string | null

  @ManyToOne(() => UserEntity, (user) => user.accounts, {
    createForeignKeyConstraints: true,
  })
  user!: UserEntity
}

@Entity({ name: "sessions" })
export class SessionEntity {
  @PrimaryGeneratedColumn("uuid")
  id!: string

  @Column({ unique: true })
  sessionToken!: string

  @Column({ type: "uuid" })
  userId!: string

  @Column({ transformer: transformer.date })
  expires!: string

  @ManyToOne(() => UserEntity, (user) => user.sessions)
  user!: UserEntity
}

@Entity({ name: "verification_tokens" })
export class VerificationTokenEntity {
  @PrimaryGeneratedColumn("uuid")
  id!: string

  @Column()
  token!: string

  @Column()
  identifier!: string

  @Column({ transformer: transformer.date })
  expires!: string
}
```

2. Pass them to `TypeORMAdapter`

```ts filename="./auth.ts"
import NextAuth from "next-auth"
import { TypeORMAdapter } from "@auth/typeorm-adapter"
import * as entities from "lib/entities"

export const { handlers, auth, signIn, signOut } = NextAuth({
  adapter: TypeORMAdapter("yourconnectionstring", { entities }),
})
```

<Callout type="info">
  The `synchronize: true` option in TypeORM will generate SQL that exactly
  matches the entities. This will automatically apply any changes it finds in
  the entity model. This is a useful option in development.
</Callout>

<Callout type="warning">
  `synchronize: true` should not be enabled against production databases as it
  may cause data loss if the configured schema does not match the expected
  schema! We recommend that you synchronize/migrate your production database at
  build-time.
</Callout>

#### Naming Conventions

If mixed snake_case and camelCase column names are an issue for you and/or your underlying database system, we recommend using TypeORM's naming strategy feature to change the target field names. There is a package called `typeorm-naming-strategies` which includes a `snake_case` strategy which will translate the fields from how Auth.js expects them, to snake_case in the actual database.

For example, you can add the naming convention option to the connection object in your NextAuth config.

```ts filename="./auth.ts"
import NextAuth from "next-auth"
import { TypeORMAdapter } from "@auth/typeorm-adapter"
import { SnakeNamingStrategy } from "typeorm-naming-strategies"
import { ConnectionOptions } from "typeorm"

const connection: ConnectionOptions = {
  type: "mysql",
  host: "localhost",
  port: 3306,
  username: "test",
  password: "test",
  database: "test",
  namingStrategy: new SnakeNamingStrategy(),
}

export const { handlers, auth, signIn, signOut } = NextAuth({
  adapter: TypeORMAdapter(connection),
})
```
