Prisma ORM の Edge Functions サポートを試す(Cloudflare Workers + GraphQL + Prisma + TiDB Serverless)
PrismaORMをCloudflare Workersで使えるようになったとのことで、開発予定のアプリのバックエンドにCloudflare Worker採用を検討。
Cloudflare Workers + GraphQL + Prisma + TiDB Serverless でバックエンド+DB構築を試す。
※思考錯誤中なので、最後まで終わったらコード等はきれいに張りなおす予定
DBはSQLを使いたかったので、調べたところTiDBでとりあえず進める。
上の記事を参考に進める。Cloudflare Worker, TiDB, Prismaの導入が終わったら、GraphQLを導入していく。
こちらを参考にしてGraphQLを導入しつつ、PrismaORMに対応させていく。
Cloudflare WorkersのTCPはリクエストごとに切れるらしいので、Contextに入れる。
import { PrismaClient } from "@prisma/client";
import { PrismaTiDBCloud } from "@tidbcloud/prisma-adapter";
import { connect } from "@tidbcloud/serverless";
const prisma = (connectionString: string) => {
const connection = connect({ url: connectionString });
const adapter = new PrismaTiDBCloud(connection);
const prisma = new PrismaClient({
adapter
});
return prisma;
};
export interface Context {
db: PrismaClient;
}
export const createContext = (connectionString: string): Context => {
return {
db: prisma(connectionString)
}
}
import { createYoga } from 'graphql-yoga';
import { schema } from './schema';
import { createContext } from './context';
interface Env {
DATABASE_URL: string;
}
const yoga = createYoga({ schema });
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const context = createContext(env.DATABASE_URL);
const response = await yoga(request, ctx);
await context.db.$disconnect();
return response;
},
};
ただし、ContextにPrismaClientを乗っけるのはVSCodeが重くなるので非推奨らしい。
とはいえ一旦これで済ませる。Pothos導入, PrismaPlugin, RelayPluginを導入してRelay準拠にする。
npm install @pothos/core @pothos/plugin-prisma @pothos/plugin-relay
import { type Context } from "./context";
import SchemaBuilder from '@pothos/core';
import PrismaPlugin from "@pothos/plugin-prisma";
import RelayPlugin from "@pothos/plugin-relay";
import type PrismaTypes from "../prisma/generated";
import { DateResolver, JSONResolver } from "graphql-scalars";
import { Prisma } from "@prisma/client";
export const builder = new SchemaBuilder<{
PrismaTypes: PrismaTypes;
Scalars: {
JSON: {
Input: unknown;
Output: unknown;
};
Date: {
Input: Date;
Output: Date;
};
};
Context: Context;
}>({
plugins: [PrismaPlugin, RelayPlugin],
relayOptions: {},
prisma: {
client: (ctx) => ctx.db,
dmmf: Prisma.dmmf
},
});
builder.queryType();
builder.mutationType();
builder.addScalarType('JSON', JSONResolver);
builder.addScalarType('Date', DateResolver);
自分はUserエンティティではなく、公式ドキュメントのLogエンティティを元に作ったので、Modelはこうなった。
import { builder } from '../builder'
const LevelEnum = builder.enumType('Level', {
values: ['Info', 'Warn', 'Error'] as const,
});
export const LogType = builder.prismaObject('Log', {
fields: (t) => ({
id: t.exposeInt("id"),
level: t.expose("level", {
type: LevelEnum
}),
message: t.exposeString("message"),
meta: t.expose("meta", {
type: 'JSON',
})
}),
})
[ERROR] service core:user:test-worker: Uncaught Error: Prisma.dmmf is not available when running in edge runtimes.
Edge FunctionだとPrisma.dmmfが使えないらしい。
でも、これを消すと型チェックで怒られる。
そもそもdmmfとは何かがわからない…。
SchemaBuilderのプラグインにPrismaPluginを入れたら怒られた。
調べても出てこず、今回のPreviewで発生するようになったエラーかもと思いDiscordで質問してみる。
こちら側で何かできそうにないので、@pothos/plugin-prisma
をやめて@pothos/plugin-simple-objects
を入れる事にした。
export const LogType = builder.simpleObject('Log', {
fields: (t) => ({
id: t.int(),
level: t.field({
type: LevelEnum
}),
message: t.string(),
meta: t.field({
type: 'JSON',
})
}),
})
export const builder = new SchemaBuilder<{
PrismaTypes: PrismaTypes;
Scalars: {
JSON: {
Input: unknown;
Output: unknown;
};
Date: {
Input: Date;
Output: Date;
};
};
Context: Context;
}>({
plugins: [SimpleObjectsPlugin],
});
いったんこれでクローズ。
githubにソース上げました。
Discordで質問したところ、同じ問題に遭遇した方がDiscordの質問までおっかけて解決法を共有してくれました!
ありがたすぎる…とりあえず試す
npm install https://github.com/repository/prisma-dmmf-generator
in schema.prisma
generator dmmf {
provider = "prisma-dmmf-generator"
output = "./generated_dmmf.json"
}
npx prisma generate
import dmmf from "../generated/dmmf.json";
const builder = new SchemaBuilder<{
Context: GraphQLContext;
PrismaTypes: PrismaTypes;
}>({
plugins: [PrismaPlugin],
prisma: {
client: (context) => context.prisma,
dmmf: dmmf,
filterConnectionTotalCount: true,
},
});
thanks m <3
GraphQLで操作する分には動いたので、クローズ。
PothosPrismaGeneratorを使ってQueryとMutationを自動生成する
wrangler dev
すると以下のエラーが発生。
✘ [ERROR] service core:user:test-worker: Uncaught TypeError: Cannot destructure property 'models' of 'this.getDMMF(...)' as it is undefined.
at null.<anonymous> (index.js:32522:13) in getModels at null.<anonymous> (index.js:32462:10) in PrismaSchemaGenerator
at null.<anonymous> (index.js:32755:22) in PothosPrismaGeneratorPlugin
at null.<anonymous> (index.js:26627:30)
at null.<anonymous> (index.js:26622:159) in BuildCache
at null.<anonymous> (index.js:28164:24) in toSchema
at null.<anonymous> (index.js:33378:22)
こっちもDMMFまわりでエラー。DMMFファイル自体は生成しているので、これを読み込む方法を調べるため、再オープン。
自動化対応は保留にしたまま開発継続したので、いったんクローズ
Property 'adapterName' is missing in type 'PrismaTiDBCloud' but required in type 'DriverAdapter'
@tidbcloud/prisma-adapterが5.11.0対応しかしてないので、@prisma/clientも5.11.0に揃える必要がある。
npm i --save-exact @prisma/client@5.11.0