4 min read

Forking typegraphql-prisma for Prisma 7

Forking typegraphql-prisma for Prisma 7

At Cerebrum, we use Prisma for typed database access and TypeGraphQL for our GraphQL schema and resolvers. In between sits typegraphql-prisma, a generator that reads our Prisma schema and emits the TypeGraphQL side of the house: models, inputs, enums, arguments, the CRUD resolvers. It's a nice setup, but it stopped being a nice setup when Prisma 7 came out.

The last upstream release of typegraphql-prisma, version 0.28.0, targets Prisma version 5.18 — and it's marked as a pre-release, so nothing newer was coming in time for us. Prisma 7 had reworked both the client generator and the DMMF (more on that in a second), so bumping the versions in package.json just gets you generated code that no longer matches what Prisma Client actually gives you. We needed to move. So we took a community fork, fixed it up, and published it as @cerebruminc/typegraphql-prisma on npm. Note that typegraphql-prisma was originally published by Michał Lytek.

Understanding Prisma's DMMF

Why typegraphql-prisma break so badly? Because typegraphql-prisma was never using a public API in the first place. It reads Prisma's generator protocol and something called the DMMF (Data Model Meta Format), which is an internal catalog of every model, operation, and input/output type that Prisma Client exposes. Because internal APIs are not stable, Prisma modified the DMMF in Prisma 7.

For example: an optional relation used to come through as UserRelationFilter. Now it's UserNullableScalarRelationFilter. The DMMF also carries operations the old generator had never heard of, such as updateManyAndReturn, which arrived in Prisma 6.2, and a generator that doesn't know those operations either drops them or breaks because it expects types that no longer exist where it expects them.

A lot of the port was teaching the generator the new DMMF vocabulary. We had to accept the new names, map them to the right TypeGraphQL classes, and then stop referencing types the DMMF no longer contains.

prisma-client vs prisma-client-js

Separate issue: Prisma 7 also changed how you get the client at all. The old generator, prisma-client-js, hands you a package you import as @prisma/client. The new one, prisma-client, writes actual TypeScript files into an output directory you pick. The fork supports both, so you don't have to choose today — but prisma-client is the direction Prisma is heading, and it's what we run.

With prisma-client-js, the imports look the same as they always did:

import { Prisma } from "@prisma/client";

With prisma-client, the generator figures out where your configured client output is and imports its /client entry instead.

A schema using both looks like this:

generator client {
  provider = "prisma-client"
  output   = "../generated/prisma"
}

generator typegraphql {
  provider           = "typegraphql-prisma"
  output             = "../generated/type-graphql"
  emitTranspiledCode = false
}

Note the provider is just typegraphql-prisma, not the scoped package name. That trips people up.

With those two output directories, a generated model imports the client over a relative path:

import { Prisma } from "../../prisma/client";

and yes, the generator does the ../ counting for you.

If you go the prisma-client route, the TypeGraphQL output has to stay TypeScript too. Keep it out of node_modules, set emitTranspiledCode = false, and make sure both generated directories are part of your app's TypeScript build. Ask for emitTranspiledCode = true and the generator refuses — that mode only compiles the TypeGraphQL files to JavaScript while their imports still point at the TypeScript Prisma Client. Broken by construction.

Returning relations from bulk mutations

updateManyAndReturn turned out to be more than one more CRUD method. When a mutation returns multiple rows, the resolver has to pass the GraphQL selection down to Prisma, otherwise whatever relations the caller asked for just don't come back.

Say someone runs:

mutation {
  updateManyAndReturnPost(data: { content: { set: "updated" } }) {
    id
    author {
      id
      name
    }
  }
}

The resolver has to build the equivalent Prisma select:

{
  select: {
    id: true,
    author: {
      select: {
        id: true,
        name: true,
      },
    },
  },
}

createManyAndReturn and updateManyAndReturn both travel through this selection path now, and their resolver files import only the helpers they actually use.

Bytes

Smaller change: Prisma hands Bytes values over as Uint8Array. The old generator mapped them to Node's Buffer, and that no longer matches the Prisma Client types.

There's a Byte GraphQL scalar for this now. On the wire it's a base64 string; in your resolvers it's a Uint8Array. It validates its own input rather than trusting Buffer.from(), which will happily digest malformed base64 without a peep.

Switching

npm install @cerebruminc/typegraphql-prisma

Version 1.0 wants Prisma ^7.8.0 and Node.js ^20.19, ^22.12, or >=24. Still on Prisma 5 or 6? Stay on an older release whose peer dependencies line up with your Prisma version.

Point the two generator blocks where you want them (see above), then run:

npx prisma generate

Fair warning: Prisma 7 changed a bunch of other things too — datasource config, module settings, how connections work. That's covered in Prisma's own upgrade guide, and it's a separate migration from this one. Do the boring reading before you bump anything.

How we test it

Snapshots are not enough for a generator. Code can match its snapshot exactly and still be wrong the first time it actually runs — wrong import, mangled selection, whatever. So the tests run the output: the Byte scalar gets executed, the selection handed to updateManyAndReturn gets checked, and an end-to-end test goes from code generation all the way to queries against a migrated Postgres database through the generated resolvers.

The package is on npm, code and issues are on GitHub, and the docs cover the rest. If you've been pinned on Prisma 5 because of this generator — that excuse just expired.

Subscribe to our newsletter

Subscribe to our newsletter to get the latest updates and news