🙄
Cloudflare, Nextjs, Authjs, Prisma, D1
Total Upload: 7486.15 KiB / gzip: 1943.68 KiB
init opennext
pnpm create cloudflare@latest cf-next-authjs-prisma-d1 --framework=next --platform=workers
prisma install
pnpm install prisma@~6.15 --save-dev
pnpm install @prisma/client@~6.15
prisma d1-adapter
pnpm install @prisma/adapter-d1@~6.15
init d1
pnpm wrangler d1 create cf-next-authjs-prisma-d1
prisma init
pnpm prisma init --datasource-provider sqlite
outputを削除
prisma/schema.prismaのclientからoutputを削除。
urlを追加
prisma/schema.prismaのdatasource dbにurl = env("DATABASE_URL")を追加。
prisma-prod.config.ts
prisma-prod.config.ts
import type { PrismaConfig } from 'prisma';
import { PrismaD1 } from '@prisma/adapter-d1';
// import your .env file
import 'dotenv/config';
export default {
experimental: {
adapter: true,
},
schema: 'prisma/schema.prisma',
async adapter() {
return new PrismaD1({
CLOUDFLARE_D1_TOKEN: process.env.CLOUDFLARE_D1_TOKEN!,
CLOUDFLARE_ACCOUNT_ID: process.env.CLOUDFLARE_ACCOUNT_ID!,
CLOUDFLARE_DATABASE_ID: process.env.CLOUDFLARE_DATABASE_ID!,
});
},
} satisfies PrismaConfig;
.env
CLOUDFLARE_ACCOUNT_ID="0773..."
CLOUDFLARE_DATABASE_ID="01f30366-..."
CLOUDFLARE_D1_TOKEN="F8Cg..."
開発用sqlteファイルの作成
pnpm wrangler d1 execute cf-next-authjs-prisma-d1 --command "select 1;"
DATABASE_URL="file:../.wrangler/state/v3/d1/miniflare-D1DatabaseObject/longfilename.sqlite"
baselining
mkdir -p prisma/migrations/0_init
touch prisma/migrations/0_init/migration.sql
pnpm prisma migrate resolve --applied 0_init
pnpm prisma migrate resolve --applied 0_init --config prisma-prod.config.ts
TableDoesNotExistエラーがでるが、Migration 0_init marked as applied.が出ればOK.
prisma for opennext
schema.prisma
generator client {
provider = "prisma-client-js"
previewFeatures = ["driverAdapters"]
}
※ prisma-client-jsではなくprisma-clientにすると、outputが必須になるため動作しなくなるため注意
next.config.ts
const nextConfig: NextConfig = {
serverExternalPackages: ["@prisma/client", ".prisma/client"],
};
lib/db.ts
//lib/db.ts
import { getCloudflareContext } from "@opennextjs/cloudflare";
// You can use cache from react to cache the client during the same request
// this is not mandatory and only has an effect for server components
import { cache } from "react";
import { PrismaClient } from "@prisma/client";
import { PrismaD1 } from "@prisma/adapter-d1";
export const getDb = cache(() => {
const { env } = getCloudflareContext();
const adapter = new PrismaD1(env.DB);
return new PrismaClient({ adapter });
});
// If you need access to `getCloudflareContext` in a static route (i.e. ISR/SSG), you should use the async version of `getCloudflareContext` to get the context.
export const getDbAsync = async () => {
const { env } = await getCloudflareContext({ async: true });
const adapter = new PrismaD1(env.DB);
const prisma = new PrismaClient({ adapter });
return prisma;
};
authjs
pnpm add next-auth@beta
pnpm add @auth/prisma-adapter
pnpm dlx auth secret
.env.localにAUTH_SECRETが生成されるので、.dev.varsにコピペする。
.env.localは消してよい。
auth.ts
import NextAuth from "next-auth"
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [],
})
app/api/auth/[...nextauth]/route.ts
import { handlers } from "@/auth" // Referring to the auth.ts we just created
export const { GET, POST } = handlers
model User {
id String @id @default(cuid())
name String?
email String? @unique
emailVerified DateTime?
image String?
accounts Account[]
sessions Session[]
// Optional for WebAuthn support
Authenticator Authenticator[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Account {
id String @id @default(cuid())
userId String
type String
provider String
providerAccountId String
refresh_token String?
access_token String?
expires_at Int?
token_type String?
scope String?
id_token String?
session_state String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([provider, providerAccountId])
}
model Session {
id String @id @default(cuid())
sessionToken String @unique
userId String
expires DateTime
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model VerificationToken {
identifier String
token String
expires DateTime
@@unique([identifier, token])
}
// Optional for WebAuthn support
model Authenticator {
credentialID String @unique
userId String
providerAccountId String
credentialPublicKey String
counter Int
credentialDeviceType String
credentialBackedUp Boolean
transports String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@id([userId, credentialID])
}
Discussion