🙄

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

https://opennext.js.org/cloudflare

prisma install

pnpm install prisma@~6.15 --save-dev
pnpm install @prisma/client@~6.15

https://www.prisma.io/docs/guides/nextjs

prisma d1-adapter

pnpm install @prisma/adapter-d1@~6.15

https://www.prisma.io/docs/orm/overview/databases/cloudflare-d1

init d1

pnpm wrangler d1 create cf-next-authjs-prisma-d1

prisma init

pnpm prisma init --datasource-provider sqlite

outputを削除

prisma/schema.prismaclientからoutputを削除。

urlを追加

prisma/schema.prismadatasource dburl = 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;
};

https://opennext.js.org/cloudflare/howtos/db#prisma-orm

authjs

pnpm add next-auth@beta
pnpm add @auth/prisma-adapter
pnpm dlx auth secret

.env.localAUTH_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])
}

https://authjs.dev/getting-started/adapters/prisma

Discussion