0.0.1 • Published 2 years ago
prisma-pgtyped-prepared-query v0.0.1
Prisma & Pgtyped client extension
This package exposes a Prisma Client extension
to use PgTyped's PreparedQuery
.
Installation
$ npm install --save @pgtyped/runtime @prisma/client prisma prisma-pgtyped-prepared-query
$ npm install --save-dev typescript @pgtyped/cli
$ pnpm add --save @pgtyped/runtime @prisma/client prisma prisma-pgtyped-prepared-query
$ pnpm add --save-dev typescript @pgtyped/cli
$ yarn add @pgtyped/runtime @prisma/client prisma prisma-pgtyped-prepared-query
$ yarn add --dev typescript @pgtyped/cli
Usage
See the packages/example folder
To use the extension, you need to call $extends
on your Prisma client:
// prisma-client.ts
import { prismaPgtypedPreparedQuery } from "prisma-pgtyped-prepared-query";
const prisma = new PrismaClient();
const prismaClient = prisma.$extends(prismaPgtypedPreparedQuery());
export type PrismaClient = typeof prismaClient;
Once you have generated your PreparedQuery
with @pgtyped/cli
, you can directly
use it with the prisma client:
// post.repository.ts
import { PrismaClient } from './prisma-client';
import {
findAllPostsRawQuery,
IFindAllPostsRawQueryResult,
} from "./find-all-posts.sql";
class PostRepository {
constructor(private readonly prisma: PrismaClient) {}
findAllPosts(clientId: string): Promise<IFindAllPostsRawQueryResult[]> {
return this.prisma.$protected(findAllPostsRawQuery).with({ clientId });
}
}
Customization
The extension use a set of classes to bridge PgTyped with Prisma. Those classes are intentionally exposed from the package for customizations.
Class name | Description |
---|---|
PrismaDatabaseConnection | The database connection implementing the PgTyped IDatabaseConnection to run the query, this is the communication layer between pgtyped and prisma |
PrismaPreparedQueryRunner | The runner adding a thin layer around the PreparedQuery to be more fluent |
For example, let's imagine that you want to validate at runtime the query output result. You could use the following snippet:
// prisma-client.ts
import { PreparedQuery } from "@pgtyped/runtime";
import { ClassConstructor, plainToInstance } from "class-transformer";
import { validateOrReject } from "class-validator";
import {
PrismaDatabaseConnection,
PrismaPreparedQueryRunner,
} from "prisma-pgtyped-prepared-query";
async function castAs<T extends object, V extends object>(
dto: ClassConstructor<T>,
rows: readonly V[]
): Promise<T[]> {
const instances = plainToInstance(dto, [...rows]);
if (instances.length > 0) await validateOrReject(instances[0]);
return instances;
}
const prisma = new PrismaClient().$extends((prisma) => {
prisma.$extends({
name: "prisma-pgtyped-validated-prepared-query",
client: {
$prepared<TInput, TOutput>(query: PreparedQuery<TInput, TOutput>) {
const runner = new PrismaPreparedQueryRunner(
new PrismaDatabaseConnection(prisma),
query
);
return {
with(params: TInput) {
return runner.with(params);
},
as<T>(dto: ClassConstructor<T>) {
return {
with: (params: TInput): Promise<T[]> => {
const rows = await this.with(params);
return castAs(dto, rows);
},
};
},
};
},
},
});
});
export type PrismaClient = typeof prisma;
// ---
// post.repository.ts
import { PrismaClient } from "./prisma-client";
import { findAllPostsRawQuery } from "./find-all-posts.sql";
class Post {
@IsUUID()
id!: string;
}
class PostRepository {
constructor(private readonly prisma: PrismaClient) {}
findAllPosts(clientId: string): Promise<Post[]> {
return this.prisma
.$prepared(findAllPostsRawQuery)
.as(Post)
.with({ clientId });
}
}
0.0.1
2 years ago