TypedSQL:使用 Prisma ORM 使您的原生 SQL 查询类型安全

原文信息: 查看原文查看原文

Announcing TypedSQL: Make your raw SQL queries type-safe with Prisma ORM

- Nikolas Burk

随着今天发布的 v5.19.0 版本,Prisma ORM 引入了一种新的方式来以类型安全的方式编写原生 SQL 查询!现在您可以同时享受到 Prisma ORM 的两大优势:适用于大多数查询的便捷高级抽象,以及适用于原生 SQL 的灵活且类型安全的逃逸舱。

快速概述:我们使原生 SQL 完全类型安全

使用 Prisma ORM,我们设计了我们认为最佳的 API 来编写常规 CRUD 查询,这些查询构成了大多数应用程序的 95%!

对于剩下的 5% —— 那些无法用 Prisma Client API 表达或需要最大性能的复杂查询 —— 我们提供了一个更低级别的 API 来编写原生 SQL。然而,这个逃逸舱没有提供类型安全性,开发者错过了他们习惯于从 Prisma ORM 获得的卓越开发体验,所以我们寻找了更好的方法!

随着今天 Prisma ORM v5.19.0 版本的发布,我们很高兴地宣布 TypedSQL:编写复杂和高性能查询的最佳方式。TypedSQL 是更好的 SQL。它是完全类型安全的,提供自动补全,并且在您需要编写原生 SQL 查询时提供出色的开发体验。以下是它的工作原理:

  1. .sql 文件中编写 SQL 查询并将其放入 prisma/sql 目录:

    -- prisma/sql/conversionByVariant.sql
    
    SELECT "variant", CAST("checked_out" AS FLOAT) / CAST("opened" 
    AS FLOAT) AS "conversion"
    FROM (
      SELECT
        "variant",
        COUNT(*) FILTER (WHERE "type"='PageOpened') AS "opened",
        COUNT(*) FILTER (WHERE "type"='CheckedOut') AS "checked_out"
      FROM "TrackingEvent"
      GROUP BY "variant"
    ) AS "counts"
    ORDER BY "conversion" DESC
    

    您还可以创建带有参数的 SQL 查询!

    -- prisma/sql/conversionByVariantByVersion.sql
    -- 注意这个语法是针对 PostgreSQL 的。
    -- 参数语法将取决于您的数据库引擎。
    
    SELECT "variant", CAST("checked_out" AS FLOAT) / CAST("opened" AS FLOAT) AS "conversion"
    FROM (
      SELECT
        "variant",
        COUNT(*) FILTER (WHERE "type"='PageOpened') AS "opened",
        COUNT(*) FILTER (WHERE "type"='CheckedOut') AS "checked_out"
      FROM "TrackingEvent"
        WHERE version = $1
      GROUP BY "variant"
    ) AS "counts"
    ORDER BY "conversion" DESC
    
  2. 使用 prisma generate 上的 --sql 标志生成查询函数:

    npx prisma generate --sql
    
  3. @prisma/client/sql 导入查询函数 …

    import { PrismaClient } from '@prisma/client'
    import { conversionByVariant } from '@prisma/client/sql'
    

    … 然后在新的 $queryRawTyped 函数中调用它以获得完全类型化的结果 😎

    // `result` 是完全类型化的!
    const result = await prisma.$queryRawTyped(conversionByVariant())
    

    如果您的 SQL 查询有参数,它们会提供给传递给 $queryRawTyped 的查询函数

    // 只给我从版本 5 的 TrackingEvent 中获取转换结果
    const result = await prisma.$queryRawTyped(conversionByVariantByVersion(5))
    

Prisma Client API 与 TypedSQL 的结合为 CRUD 操作和高度复杂的查询提供了最佳体验。有了这个新增功能,我们希望您再也不需要触摸 SQL 查询构建器了!

高级抽象带来高生产力

原生 SQL 仍然是查询关系数据库中最强大和灵活的方式。但它确实有一些缺点。

原生 SQL 的缺点

如果您之前在 TypeScript 项目中编写过原生 SQL,您可能知道它并不提供最佳的开发体验:

  • 编写 SQL 查询时没有自动补全。
  • 查询结果没有类型安全性。
  • 编写和调试复杂 SQL 查询的复杂性。
  • 开发团队通常具有不同水平的 SQL 经验,不是每个团队成员都擅长编写 SQL。
  • SQL 使用不同的数据模型(关系)与 TypeScript(对象)相比,需要从一种映射到另一种;这在处理模型之间的关系时尤为明显,这些关系在 SQL 中通过 外键 表示,但在 TypeScript 中作为 嵌套对象 表示。

应用开发者应该关心 数据 —— 而不是 SQL

在 Prisma,我们坚信应用开发者应该关心 数据 —— 而不是 SQL。

典型应用开发者编写的大多数查询使用的功能相当有限,通常与常见的 CRUD 操作有关,如 分页过滤器嵌套查询

我们的主要目标是确保应用开发者能够快速获得他们需要的数据,而不需要过多考虑查询和将数据库中的行映射到他们代码中的对象。

使用 Prisma ORM 快速发货

这就是我们构建 Prisma ORM 的原因,为开发者提供一种抽象,使他们高效并快速发货!以下是使用 Prisma ORM 的典型工作流程概述。

首先,您在一个人类可读的模式中定义您的数据模型:

model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
  posts Post[]
}

model Post {
  id        Int     @id @default(autoincrement())
  title     String
  content   String?
  published Boolean @default(false)
  author    User    @relation(fields: [authorId], references: [id])
  authorId  Int
}

然后,您可以使用 Prisma CLI 生成(可定制的)SQL 迁移,并针对数据库运行迁移。一旦模式映射到您的数据库,您可以使用 Prisma Client 进行查询:

// 创建带有帖子的新用户
await prisma.user.create({
  data: {
    name: 'Alice',
    email: 'alice@prisma.io',
    posts: {
      create: { title: 'Hello World' },
    },
  },
})

逃逸舱:下降到原生 SQL

虽然我们相信这种更高级的抽象使开发者更有生产力,但我们已经看到许多项目需要编写原生 SQL 的选项。这通常发生在:

  • Prisma Client API 无法灵活地表达某个查询。
  • 查询需要为速度进行优化。

在这些情况下,Prisma ORM 通过使用 Prisma Client 的 $queryRaw 方法提供了一个原生 SQL 的逃逸舱:

const result = await prisma.$queryRaw`
SELECT "variant", CAST("checked_out" AS FLOAT) / CAST("opened" AS FLOAT) AS "conversion"
FROM (
  SELECT
    "variant",
    COUNT(*) FILTER (WHERE "type"='PageOpened') AS "opened",
    COUNT(*) FILTER (WHERE "type"='CheckedOut') AS "checked_out"
  FROM "TrackingEvent"
  GROUP BY "variant"
) AS "counts"
ORDER BY "conversion" DESC
`

这种方法的主要问题是这个查询不是类型安全的。如果开发者想要享受标准 Prisma Client API 提供的类型安全性好处,他们需要手动编写这个查询的返回类型,这可能既繁琐又耗时。另一个问题是这些手动定义的类型不会随着模式变化自动更新,这引入了另一个错误的可能性。

虽然有方法可以通过 Prisma ORM 的原生查询来提高开发体验,例如使用 Kysely 查询构建器扩展 for Prisma CientSafeQL,但我们希望以原生方式解决这个问题。

Prisma ORM 新增功能:TypedSQL 🎉

这就是为什么我们很高兴地介绍 TypedSQL,Prisma ORM 中的一个新工作流程,为您提供原生 SQL 查询的类型安全性。TypedSQL 的灵感来自像 PgTypedsqlx 这样的项目,它们基于类似的想法。

有了 TypedSQL,Prisma ORM 现在为您提供了最好的两全其美:

  • 一个使开发者高效且能够服务于项目中大多数查询的高级抽象。
  • 当您需要直接编写 SQL 时,一个令人愉快且类型安全的逃逸舱。

它还为开发团队提供了选择他们最喜欢的方法的选项:您的团队中是否有工程师是铁杆 SQL 粉丝,但也有些人不愿意接触 SQL?

Prisma ORM 现在为两组人都提供了他们想要的东西,而不会牺牲开发体验或灵活性!

分享于 2024-09-08

访问量 82

预览图片