Skip to content
Migrating from NextAuth.js v4? Read our migration guide.

Kysely Adapter

리소스

설정

설치

npm install kysely @auth/kysely-adapter

환경 변수

DATABASE_HOST=
DATABASE_NAME=
DATABASE_USER=
DATABASE_PASSWORD=

설정

이 어댑터는 Kysely(v0.24.2 기준)가 지원하는 PostgreSQL, MySQL, SQLite와 같은 주요 데이터베이스 방언을 지원합니다. 아래 예제는 pg 클라이언트를 사용한 PostgreSQL을 기준으로 작성되었습니다.

npm install pg
npm install --save-dev @types/pg
./auth.ts
import NextAuth from "next-auth"
import { KyselyAdapter } from "@auth/kysely-adapter"
import { db } from "../../../db"
 
export const { handlers, auth, signIn, signOut } = NextAuth({
  adapter: KyselyAdapter(db),
  providers: [],
})

Kysely의 생성자는 각 테이블에 대한 인터페이스를 포함하는 데이터베이스 인터페이스를 필요로 합니다. 이 타입들은 수동으로 정의하거나 kysely-codegen 또는 prisma-kysely를 사용해 자동으로 생성할 수 있습니다. Auth.js에서 요구하는 기본 모델을 확인해 보세요.

db.ts
import { PostgresDialect } from "kysely"
import { Pool } from "pg"
 
// 이 어댑터는 원래 `Kysely` 클래스를 래핑한 `KyselyAuth`를 내보냅니다.
// 이는 추가적인 타입 안전성을 제공하기 위해 사용할 수 있습니다.
// 필수는 아니지만, 데이터베이스 인터페이스가 Auth.js가 기대하는 모든 필드를 가지고 있는지 확인하기 위해 사용하는 것이 좋습니다.
import { KyselyAuth } from "@auth/kysely-adapter"
 
import type { GeneratedAlways } from "kysely"
 
interface Database {
  User: {
    id: GeneratedAlways<string>
    name: string | null
    email: string
    emailVerified: Date | null
    image: string | null
  }
  Account: {
    id: GeneratedAlways<string>
    userId: string
    type: string
    provider: string
    providerAccountId: string
    refresh_token: string | null
    access_token: string | null
    expires_at: number | null
    token_type: string | null
    scope: string | null
    id_token: string | null
    session_state: string | null
  }
  Session: {
    id: GeneratedAlways<string>
    userId: string
    sessionToken: string
    expires: Date
  }
  VerificationToken: {
    identifier: string
    token: string
    expires: Date
  }
}
 
export const db = new KyselyAuth<Database>({
  dialect: new PostgresDialect({
    pool: new Pool({
      host: process.env.DATABASE_HOST,
      database: process.env.DATABASE_NAME,
      user: process.env.DATABASE_USER,
      password: process.env.DATABASE_PASSWORD,
    }),
  }),
})
💡

타입을 수동으로 정의하는 대신, 데이터베이스 스키마에서 kysely-codegen을 사용하거나 Prisma 스키마에서 prisma-kysely를 사용해 자동으로 생성할 수도 있습니다. KyselyAuth와 함께 생성된 타입을 사용할 때는 Codegen을 임포트하고 두 번째 제네릭 인자로 전달하세요:

import type { Codegen } from "@auth/kysely-adapter"
new KyselyAuth<Database, Codegen>()

스키마

db/migrations/001_create_db.ts
import { Kysely, sql } from "kysely"
 
export async function up(db: Kysely<any>): Promise<void> {
  await db.schema
    .createTable("User")
    .addColumn("id", "uuid", (col) =>
      col.primaryKey().defaultTo(sql`gen_random_uuid()`)
    )
    .addColumn("name", "text")
    .addColumn("email", "text", (col) => col.unique().notNull())
    .addColumn("emailVerified", "timestamptz")
    .addColumn("image", "text")
    .execute()
 
  await db.schema
    .createTable("Account")
    .addColumn("id", "uuid", (col) =>
      col.primaryKey().defaultTo(sql`gen_random_uuid()`)
    )
    .addColumn("userId", "uuid", (col) =>
      col.references("User.id").onDelete("cascade").notNull()
    )
    .addColumn("type", "text", (col) => col.notNull())
    .addColumn("provider", "text", (col) => col.notNull())
    .addColumn("providerAccountId", "text", (col) => col.notNull())
    .addColumn("refresh_token", "text")
    .addColumn("access_token", "text")
    .addColumn("expires_at", "bigint")
    .addColumn("token_type", "text")
    .addColumn("scope", "text")
    .addColumn("id_token", "text")
    .addColumn("session_state", "text")
    .execute()
 
  await db.schema
    .createTable("Session")
    .addColumn("id", "uuid", (col) =>
      col.primaryKey().defaultTo(sql`gen_random_uuid()`)
    )
    .addColumn("userId", "uuid", (col) =>
      col.references("User.id").onDelete("cascade").notNull()
    )
    .addColumn("sessionToken", "text", (col) => col.notNull().unique())
    .addColumn("expires", "timestamptz", (col) => col.notNull())
    .execute()
 
  await db.schema
    .createTable("VerificationToken")
    .addColumn("identifier", "text", (col) => col.notNull())
    .addColumn("token", "text", (col) => col.notNull().unique())
    .addColumn("expires", "timestamptz", (col) => col.notNull())
    .execute()
 
  await db.schema
    .createIndex("Account_userId_index")
    .on("Account")
    .column("userId")
    .execute()
 
  await db.schema
    .createIndex("Session_userId_index")
    .on("Session")
    .column("userId")
    .execute()
}
 
export async function down(db: Kysely<any>): Promise<void> {
  await db.schema.dropTable("Account").ifExists().execute()
  await db.schema.dropTable("Session").ifExists().execute()
  await db.schema.dropTable("User").ifExists().execute()
  await db.schema.dropTable("VerificationToken").ifExists().execute()
}

이 스키마는 Kysely에서 사용할 수 있도록 조정되었으며, 주요 스키마를 기반으로 합니다.

Kysely를 사용하여 마이그레이션을 생성하고 실행하는 방법에 대한 자세한 내용은 Kysely 마이그레이션 문서를 참조하세요.

명명 규칙

스네이크 케이스(snake_case)와 카멜 케이스(camelCase) 컬럼 이름이 혼합되어 문제가 되거나 데이터베이스 시스템에 영향을 미치는 경우, Kysely의 CamelCasePlugin(문서 참조) 기능을 사용하여 필드 이름을 변경하는 것을 권장합니다. 이 방법은 NextAuth.js에는 영향을 미치지 않지만, Kysely를 사용할 때 일관된 케이스를 유지할 수 있게 해줍니다.

Auth.js © Balázs Orbán and Team - 2025