额外
rspc 集成

rspc 集成

Prisma Client Rust 对 rspc (在新标签页中打开) 提供了一流的支持,rspc 是一种与服务器无关的路由器,可以自动生成 TypeScript 绑定。

这些示例使用以下模式

model Post {
    id        String   @id @default(cuid())
    title     String
 
    comments Comment[]
}
 
model Comment {
    id        String   @id @default(cuid())
    content   String
 
    post   Post   @relation(fields: [postID], references: [id])
    postID String
}

设置

rspc 特性同时在 prisma-client-rustprisma-client-rust-cli 中启用时,PCR 生成的所有数据类型都将实现 specta::Type,并且会为 rspc::Error 生成 From<queries::Error> 实现。

查询

将 PCR 和 rspc 结合使用时,以下操作是可能的

use rspc::Router;
 
fn main() -> Router {
    Router::<prisma::PrismaClient>::new()
        .query("posts", |db, _: ()| async move {
            let posts = db
                .post()
                .find_many(vec![])
                .exec()
                .await?;
 
            Ok(posts)
        })
        .build()
}

当生成 TypeScript 时,它看起来会像这样

export type Operations = {
    queries: { key: ["posts"], result: Array<Post> }
};
 
export interface Post { id: string, title: string }

这也适用于 选择 & 包含

use rspc::Router;
 
fn main() -> Router {
    Router::<prisma::PrismaClient>::new()
        .query("posts", |db, _: ()| async move {
            let posts = db
                .post()
                .find_many(vec![])
                .select(post::select!({
                    id
                    title
                }))
                .exec()
                .await?;
 
            Ok(posts)
        })
        .build()
}

生成的 TypeScript 看起来会像这样

export type Operations = {
    queries: { key: ["posts"], result: Array<{ id: string, title: string}> }
};

从 0.6.4 开始,将为命名的 include/select 生成专用的类型。但是,这些类型仅在特殊情况下提供,建议您使用 rspc(如 rspc.inferProcedureResult)中的辅助函数来访问过程结果,而不是依赖于命名类型。

关系类型

需要注意的是,为模型生成的类型没有关系的字段。这样做是为了在使用 include 时提供更好的体验,但以使用 with/fetch 获取关系时的体验较差为代价。

做出这种权衡是因为我们在构建 Spacedrive (在新标签页中打开) 时的经验。与其让我们的所有 TypeScript 函数在运行时都必须验证关系是否已加载(使用 with/fetch 时的情况),不如让每个函数都显式指定它希望加载的关系。

import { User, Post } from "./exported-types"
 
// Only recieves scalar fields of User
function needsOneUser(user: User) {}
 
// Receives scalar fields + posts relation of User
function needsOneUserWithPosts(user: User & { posts: Post[] }) {}

这种方法有一些缺点

  • 需要知道您在 TypeScript 中包含的关系的名称和类型(尽管正在对此进行改进)
  • with/fetch 可以动态修改
  • with/fetch 提供了更好的编辑器自动完成功能

但是,我们发现这些缺点被拥有跨后端和前端的完整类型安全性的优势所抵消。

错误处理

由于 rspc::Error 可以实现 From<queries::Error>,因此可以使用 Rust 的所有常规错误处理约定。

? 运算符可用于提取成功值,并在遇到错误时提前返回,自动将 Prisma 错误转换为 rspc 错误。这会导致在从解析器返回之前需要将提取的值包装在 Ok 中。

另一种方法是在从解析器返回之前添加 .map_err(Into::into)。这会导致 Prisma 错误转换为 rspc 错误,同时保持 Result 不变,如下所示

use rspc::Router;
 
fn main() -> Router {
    Router::<prisma::PrismaClient>::new()
        .query("posts", |db, _: ()| async move {
            db
                .post()
                .find_many(vec![])
                .exec()
                .await
                .map_err(Into::into)
        })
        .build()
}

有关 rpsc 中错误处理的详细信息,请参阅 文档 (在新标签页中打开)