Skip to main content

如何使用 Prisma ORM 和 Prisma Postgres 与 Elysia 的集成

15 min

介绍

¥Introduction

Elysia 是一个符合人机工程学的 Web 框架,用于使用 Bun 构建高性能后端服务器。它提供端到端的类型安全、富有表现力的 API 和卓越的性能。结合 Prisma ORM 和 Prisma Postgres,你可以获得一个快速、类型安全的后端堆栈。

¥Elysia is an ergonomic web framework for building high-performance backend servers with Bun. It offers end-to-end type safety, an expressive API, and exceptional performance. Combined with Prisma ORM and Prisma Postgres, you get a fast, type-safe backend stack.

在本指南中,你将学习如何在 Elysia 应用中将 Prisma ORM 与 Prisma Postgres 数据库集成。你可以在 GitHub 上找到本指南的完整示例。

¥In this guide, you'll learn to integrate Prisma ORM with a Prisma Postgres database in an Elysia application. You can find a complete example of this guide on GitHub.

先决条件

¥Prerequisites

  • Bun 已安装在你的系统中

    ¥Bun installed on your system

1. 设置你的项目

¥ Set up your project

使用 Bun 脚手架命令创建一个新的 Elysia 项目:

¥Create a new Elysia project using the Bun scaffolding command:

bun create elysia elysia-prisma

导航到项目目录:

¥Navigate to the project directory:

cd elysia-prisma

2. 安装和配置 Prisma

¥ Install and configure Prisma

2.1.安装依赖

¥2.1. Install dependencies

安装所需的 Prisma 包、数据库适配器和 Prismabox(用于生成 TypeBox 模式):

¥Install the required Prisma packages, database adapter, and Prismabox (for generated TypeBox schemas):

bun add -d prisma bun-types
bun add @prisma/client @prisma/adapter-pg pg prismabox
信息

如果你使用其他数据库提供商(MySQL、SQL Server、SQLite),请安装相应的驱动程序适配器包,而不是 @prisma/adapter-pg。欲了解更多信息,请参阅 数据库驱动程序

¥If you are using a different database provider (MySQL, SQL Server, SQLite), install the corresponding driver adapter package instead of @prisma/adapter-pg. For more information, see Database drivers.

安装完成后,请在你的项目中初始化 Prisma:

¥Once installed, initialize Prisma in your project:

bunx prisma init --db --output ../src/generated/prisma
信息

在设置 Prisma Postgres 数据库时,你需要回答几个问题。选择离你最近的区域,并为数据库选择一个容易记住的名称,例如 "我的 Elysia 项目"

¥You'll need to answer a few questions while setting up your Prisma Postgres database. Select the region closest to your location and a memorable name for your database like "My Elysia Project"

这将造成:

¥This will create:

  • 一个包含 schema.prisma 文件的 prisma/ 目录

    ¥A prisma/ directory with a schema.prisma file

  • 用于配置 Prisma 的 prisma.config.ts 文件

    ¥A prisma.config.ts file for configuring Prisma

  • 一个已设置 DATABASE_URL.env 文件

    ¥A .env file with a DATABASE_URL already set

2.2.定义 Prisma Schema

¥2.2. Define your Prisma Schema

prisma/schema.prisma 文件中,使用 Prismabox TypeBox 生成器和 Todo 模型镜像示例应用结构:

¥In the prisma/schema.prisma file, mirror the example app structure with Prismabox TypeBox generators and a Todo model:

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

generator prismabox {
provider = "prismabox"
typeboxImportDependencyName = "elysia"
typeboxImportVariableName = "t"
inputModel = true
output = "../src/generated/prismabox"
}

datasource db {
provider = "postgresql"
}

model Todo {
id Int @id @default(autoincrement())
title String
completed Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}

这与 Prisma Elysia 示例相符:它会生成 Prisma Client 到 src/generated/prisma 的架构,以及 Prismabox TypeBox 到 src/generated/prismabox 的架构。

¥This matches the Prisma Elysia example: it generates Prisma Client to src/generated/prisma and Prismabox TypeBox schemas to src/generated/prismabox.

什么是 Prismabox?

¥What is Prismabox?

  • 一个 Prisma 生成器,读取你的 Prisma schema 并生成 Elysia 兼容的 TypeBox 模型。

    ¥A Prisma generator that reads your Prisma schema and emits Elysia-friendly TypeBox models.

  • 生成类似 src/generated/prismabox/Todo.ts 的文件(每个模型一个),以及 TodoPlainTodoPlainInputCreate 等。

    ¥Generates files like src/generated/prismabox/Todo.ts (and one per model) with TodoPlain, TodoPlainInputCreate, etc.

  • 在路由中使用这些生成的模型来验证请求/响应,并使 Elysia 类型与你的 Prisma schema 保持同步(也适用于 OpenAPI/Eden)。

    ¥Use those generated models in routes to validate requests/responses and keep Elysia types in sync with your Prisma schema (also useful for OpenAPI/Eden).

2.3.运行迁移并生成 Prisma 客户端

¥2.3. Run migrations and generate Prisma Client

运行以下命令以创建数据库表并生成 Prisma 客户端:

¥Run the following commands to create the database tables and generate the Prisma Client:

bunx prisma migrate dev --name init
bunx prisma generate

2.4.种子数据库

¥2.4. Seed the database

添加一些种子数据,以使用示例待办事项填充数据库(镜像示例仓库)。

¥Add some seed data to populate the database with sample todos (mirrors the example repo).

prisma/ 目录中创建一个名为 seed.ts 的新文件:

¥Create a new file called seed.ts in the prisma/ directory:

prisma/seed.ts
import { PrismaClient } from "../src/generated/prisma/client.js";
import { PrismaPg } from "@prisma/adapter-pg";

if (!process.env.DATABASE_URL) {
throw new Error("DATABASE_URL is not set");
}

const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
const prisma = new PrismaClient({ adapter });

const todoData = [
{ title: "Learn Elysia" },
{ title: "Learn Prisma" },
{ title: "Build something awesome", completed: true },
];

async function main() {
console.log("Start seeding...");
for (const todo of todoData) {
const created = await prisma.todo.create({
data: todo,
});
console.log(`Created todo with id: ${created.id}`);
}
console.log("Seeding finished.");
}

main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});

运行种子脚本:

¥Run the seed script:

bunx prisma db seed

打开 Prisma Studio 检查你的数据:

¥And open Prisma Studio to inspect your data:

bunx prisma studio

3. 将 Prisma 集成到 Elysia

¥ Integrate Prisma into Elysia

3.1.创建 Prisma 客户端实例

¥3.1. Create a Prisma Client instance

src/ 目录中,创建一个 lib 目录,并在其中创建一个 prisma.ts 文件。此文件将创建并导出你的 Prisma Client 实例,并添加以下代码:

¥Inside the src/ directory, create a lib directory with a prisma.ts file. This file will create and export your Prisma Client instance and add the following code:

src/lib/prisma.ts
import { PrismaClient } from "../generated/prisma/client.js";
import { PrismaPg } from "@prisma/adapter-pg";

const databaseUrl = process.env.DATABASE_URL;

if (!databaseUrl) {
throw new Error("DATABASE_URL is not set");
}

const adapter = new PrismaPg({ connectionString: databaseUrl });
export const prisma = new PrismaClient({ adapter });

3.2.创建 API 路由

¥3.2. Create API routes

更新你的 src/index.ts 文件,使其与 Prisma Elysia 示例匹配,包括 Prismabox 生成的验证类型:

¥Update your src/index.ts file to match the Prisma Elysia example, including Prismabox-generated validation types:

src/index.ts
import { Elysia, t } from "elysia";
import { prisma } from "./lib/prisma";
import {
TodoPlain,
TodoPlainInputCreate,
TodoPlainInputUpdate,
} from "./generated/prismabox/Todo";

const app = new Elysia()
// Health check
.get("/", () => {
return { message: "Hello Elysia with Prisma!" };
})

// Get all todos
.get(
"/todos",
async () => {
const todos = await prisma.todo.findMany({
orderBy: { createdAt: "desc" },
});
return todos;
},
{
response: t.Array(TodoPlain),
}
)

// Get a single todo by ID
.get(
"/todos/:id",
async ({ params, set }) => {
const id = Number(params.id);
const todo = await prisma.todo.findUnique({
where: { id },
});

if (!todo) {
set.status = 404;
return { error: "Todo not found" };
}

return todo;
},
{
params: t.Object({
id: t.Numeric(),
}),
response: {
200: TodoPlain,
404: t.Object({
error: t.String(),
}),
},
}
)

// Create a new todo
.post(
"/todos",
async ({ body }) => {
const todo = await prisma.todo.create({
data: {
title: body.title,
},
});
return todo;
},
{
body: TodoPlainInputCreate,
response: TodoPlain,
}
)

// Update a todo
.put(
"/todos/:id",
async ({ params, body, set }) => {
const id = Number(params.id);

try {
const todo = await prisma.todo.update({
where: { id },
data: {
title: body.title,
completed: body.completed,
},
});
return todo;
} catch {
set.status = 404;
return { error: "Todo not found" };
}
},
{
params: t.Object({
id: t.Numeric(),
}),
body: TodoPlainInputUpdate,
response: {
200: TodoPlain,
404: t.Object({
error: t.String(),
}),
},
}
)

// Toggle todo completion
.patch(
"/todos/:id/toggle",
async ({ params, set }) => {
const id = Number(params.id);

try {
const todo = await prisma.todo.findUnique({
where: { id },
});

if (!todo) {
set.status = 404;
return { error: "Todo not found" };
}

const updated = await prisma.todo.update({
where: { id },
data: { completed: !todo.completed },
});

return updated;
} catch {
set.status = 404;
return { error: "Todo not found" };
}
},
{
params: t.Object({
id: t.Numeric(),
}),
response: {
200: TodoPlain,
404: t.Object({
error: t.String(),
}),
},
}
)

// Delete a todo
.delete(
"/todos/:id",
async ({ params, set }) => {
const id = Number(params.id);

try {
const todo = await prisma.todo.delete({
where: { id },
});
return todo;
} catch {
set.status = 404;
return { error: "Todo not found" };
}
},
{
params: t.Object({
id: t.Numeric(),
}),
response: {
200: TodoPlain,
404: t.Object({
error: t.String(),
}),
},
}
)

.listen(3000);

console.log(
`🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
);

这将创建与官方示例相同的端点:

¥This creates the same endpoints as the official example:

  • GET / - 健康检查

    ¥GET / - Health check

  • GET /todos - 获取所有待办事项(按最新排序)

    ¥GET /todos - Fetch all todos (newest first)

  • GET /todos/:id - 获取单个待办事项

    ¥GET /todos/:id - Fetch a single todo

  • POST /todos - 创建待办事项

    ¥POST /todos - Create a todo

  • PUT /todos/:id - 更新待办事项

    ¥PUT /todos/:id - Update a todo

  • PATCH /todos/:id/toggle - 切换代码补全

    ¥PATCH /todos/:id/toggle - Toggle completion

  • DELETE /todos/:id - 删除待办事项

    ¥DELETE /todos/:id - Delete a todo

Prismabox 生成 TodoPlain/TodoPlainInput* TypeBox 模式,以便验证响应和请求体的类型。

¥Prismabox generates the TodoPlain/TodoPlainInput* TypeBox schemas so responses and request bodies are validated and typed.

3.3.运行应用

¥3.3. Run the application

启动你的 Elysia 服务器:

¥Start your Elysia server:

bun run dev

你应该在控制台中看到 🦊 Elysia is running at localhost:3000

¥You should see 🦊 Elysia is running at localhost:3000 in the console.

3.4.测试 API

¥3.4. Test the API

使用 curl 测试端点:

¥Test the endpoints using curl:

# Health check
curl http://localhost:3000/ | jq

# Get all todos
curl http://localhost:3000/todos | jq

# Get a single todo
curl http://localhost:3000/todos/1 | jq

# Create a new todo
curl -X POST http://localhost:3000/todos \
-H "Content-Type: application/json" \
-d '{"title": "Ship the Prisma + Elysia guide"}' | jq

# Toggle completion
curl -X PATCH http://localhost:3000/todos/1/toggle | jq

# Update a todo
curl -X PUT http://localhost:3000/todos/1 \
-H "Content-Type: application/json" \
-d '{"title": "Updated title", "completed": true}' | jq

# Delete a todo
curl -X DELETE http://localhost:3000/todos/1 | jq

你已完成!你已使用 Prisma 创建了一个连接到 Prisma Postgres 数据库的 Elysia 应用。

¥You're done! You've created an Elysia app with Prisma that's connected to a Prisma Postgres database.

下一步

¥Next steps

现在,你的 Elysia 应用已连接到 Prisma Postgres 数据库,你可以执行以下操作:

¥Now that you have a working Elysia app connected to a Prisma Postgres database, you can:

  • 使用更多模型和关系扩展你的 Prisma 模式

    ¥Extend your Prisma schema with more models and relationships

  • 添加更新和删除端点

    ¥Add update and delete endpoints

  • 探索 Elysia 插件 的身份验证

    ¥Explore authentication with Elysia plugins

  • 使用 Prisma Postgres 启用查询缓存以获得更好的性能

    ¥Enable query caching with Prisma Postgres for better performance

  • 使用 Eden 进行端到端类型安全的 API 调用

    ¥Use Eden for end-to-end type-safe API calls

更多信息

¥More info


Stay connected with Prisma

Continue your Prisma journey by connecting with our active community. Stay informed, get involved, and collaborate with other developers:

We genuinely value your involvement and look forward to having you as part of our community!