Get type of prisma client before it is defined

In my next.js app I have to use the following approach to prevent multiple instances of PrismaClient being created
import { PrismaClient } from "@prisma/client";

const extension = {
model: {
user: {
example: () => "example",
},
},
};

const globalForPrisma = globalThis as unknown as { db: PrismaClient };
const db = globalForPrisma.db || new PrismaClient().$extends(extension);

export default db;

if (process.env.NODE_ENV !== "production") globalForPrisma.db = db;
import { PrismaClient } from "@prisma/client";

const extension = {
model: {
user: {
example: () => "example",
},
},
};

const globalForPrisma = globalThis as unknown as { db: PrismaClient };
const db = globalForPrisma.db || new PrismaClient().$extends(extension);

export default db;

if (process.env.NODE_ENV !== "production") globalForPrisma.db = db;
The above approach however is problematic because it doesn't generate the correct PrismaClient type. E.g. in my project TS will complain that db.user.example doesn't exist. I tried solving this with typeof new PrismaClient().$extends(extension) but I need globalForPrisma to be defined before the PrismaClient has been defined
10 Replies
ncls.
ncls.5mo ago
I use the following solution in a file called prisma.ts where I then import my client from:
import { PrismaClient } from '@prisma/client';

declare global {
var prisma: PrismaClient | undefined;
}

const prisma = globalThis.prisma || new PrismaClient();

if (process.env.NODE_ENV !== 'production') globalThis.prisma = prisma;

export default prisma;
import { PrismaClient } from '@prisma/client';

declare global {
var prisma: PrismaClient | undefined;
}

const prisma = globalThis.prisma || new PrismaClient();

if (process.env.NODE_ENV !== 'production') globalThis.prisma = prisma;

export default prisma;
nikitarevenco
nikitarevencoOP5mo ago
Thank you! I found an alternative solution without having to use declarations (someone told me they're unsafe).
const extension = {/*...*/}
const createPrismaClient = () => {
return new PrismaClient().$extends(extension);
};

const globalForPrisma = globalThis as unknown as {
db: ReturnType<typeof createPrismaClient>;
};
const db = globalForPrisma.db || createPrismaClient();
const extension = {/*...*/}
const createPrismaClient = () => {
return new PrismaClient().$extends(extension);
};

const globalForPrisma = globalThis as unknown as {
db: ReturnType<typeof createPrismaClient>;
};
const db = globalForPrisma.db || createPrismaClient();
nikitarevenco
nikitarevencoOP5mo ago
I am getting this error though
No description
ncls.
ncls.5mo ago
Are you trying to run prisma in the middleware? That doesn't work since the middlware is run on the edge runtime which Prisma can't do
nikitarevenco
nikitarevencoOP5mo ago
This isn't on the middleware, it's in prisma/index.ts. This error doesn't happen if I remove the createPrismaClient function. e.g.: No Error:
const globalForPrisma = globalThis as unknown as {
db: PrismaClient;
};

const db = globalForPrisma.db || new PrismaClient();
const globalForPrisma = globalThis as unknown as {
db: PrismaClient;
};

const db = globalForPrisma.db || new PrismaClient();
Yes Error:
const createPrismaClient = () => {
return new PrismaClient().$extends(extension);
};

const globalForPrisma = globalThis as unknown as {
db: ReturnType<typeof createPrismaClient>;
};

const db = globalForPrisma.db || createPrismaClient()
const createPrismaClient = () => {
return new PrismaClient().$extends(extension);
};

const globalForPrisma = globalThis as unknown as {
db: ReturnType<typeof createPrismaClient>;
};

const db = globalForPrisma.db || createPrismaClient()
ncls.
ncls.5mo ago
Then try my code. I don't know why it would be unsafe to use it.
nikitarevenco
nikitarevencoOP5mo ago
Your code has the same problem as mine though, since you are setting prisma: PrismaClient if you want your prisma client to have an extension it will be of the wrong type. eg:
import { PrismaClient } from '@prisma/client';

const extension = { /* ... */ }

declare global {
var prisma: PrismaClient | undefined;
}

const prisma = globalThis.prisma || new PrismaClient().$extends(extension);

if (process.env.NODE_ENV !== 'production') globalThis.prisma = prisma;

export default prisma;
import { PrismaClient } from '@prisma/client';

const extension = { /* ... */ }

declare global {
var prisma: PrismaClient | undefined;
}

const prisma = globalThis.prisma || new PrismaClient().$extends(extension);

if (process.env.NODE_ENV !== 'production') globalThis.prisma = prisma;

export default prisma;
You see, we have added an extension to PrismaClient but the type of prisma is still PrismaClient, so like if our extension has a method then typescript won't know about it Ideally we can somehow set var prisma: PrismaClientWithExtension<typeof extension> or something like that. Not sure if it's a thing though
ncls.
ncls.5mo ago
You can create a type which has all the additional typing and then do
type PrismaClientWithExtension = PrismaClient & {
// All your additional attributes...
}

declare global {
var prisma: PrismaClientWithExtension | undefined;
}
type PrismaClientWithExtension = PrismaClient & {
// All your additional attributes...
}

declare global {
var prisma: PrismaClientWithExtension | undefined;
}
nikitarevenco
nikitarevencoOP5mo ago
That's a cool idea but I tried it and it doesn't work unfortunately. My extension is like this:
const extension = {
model: {
user: {
example: () => {},
},
},
};
const extension = {
model: {
user: {
example: () => {},
},
},
};
No description
nikitarevenco
nikitarevencoOP5mo ago
What actually happens is TS thinks that db has db.model which shouldn't happen
Want results from more Discord servers?
Add your server