如何在 TanStack Start 中使用 Prisma ORM
简介
Prisma ORM 简化了数据库交互,而 TanStack Start 为构建现代 React 应用程序提供了强大的框架。结合 Prisma Postgres,它们提供无缝的全栈开发体验,支持类型安全的查询和高效的数据管理。
本指南将引导您从头开始,在 TanStack Start 项目中集成 Prisma ORM 和 Prisma Postgres 数据库。
先决条件
1. 设置您的项目
首先,创建一个新的 TanStack Start 项目。
为了本指南的目的,我们使用的设置说明与您在 TanStack Start 文档 中找到的相同。
在您希望创建项目的目录中,运行以下命令:
mkdir tanstack-start-prisma
cd tanstack-start-prisma
npm init -y
这将在当前目录中创建一个名为 tanstack-start-prisma
的新文件夹,然后进入该文件夹并初始化一个新的 Node.js 项目。
在您的 IDE 中打开该目录,并创建一个 tsconfig.json
文件,配置如下:
{
"compilerOptions": {
"jsx": "react-jsx",
"moduleResolution": "Bundler",
"module": "ESNext",
"target": "ES2022",
"skipLibCheck": true,
"strictNullChecks": true
}
}
我们还需要一个 .gitignore
文件,现在就来设置它:
node_modules
.env
app/generated
接下来,安装 TanStack Router 和 Vinxi,因为 TanStack Start 目前需要它们:
npm install @tanstack/react-start @tanstack/react-router vinxi
我们还需要 React、Vite React 插件和 TypeScript:
npm install react react-dom
npm install --save-dev @vitejs/plugin-react vite-tsconfig-paths
npm install --save-dev typescript @types/react @types/react-dom
更新您的 package.json
以使用 Vinxi 的 CLI。添加 "type": "module"
并修改脚本以使用 Vinxi 的 CLI:
{
"name": "tanstack-start-prisma",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"scripts": {
"dev": "vinxi dev",
"build": "vinxi build",
"start": "vinxi start"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"@tanstack/react-router": "^1.119.0",
"@tanstack/react-start": "^1.119.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"vinxi": "^0.5.6"
},
"devDependencies": {
"@types/react": "^19.1.2",
"@types/react-dom": "^19.1.3",
"@vitejs/plugin-react": "^4.4.1",
"typescript": "^5.8.3",
"vite-tsconfig-paths": "^5.1.4"
}
}
然后,创建并配置 TanStack Start 的 app.config.ts
文件:
import { defineConfig } from '@tanstack/react-start/config'
import tsConfigPaths from 'vite-tsconfig-paths'
export default defineConfig({
vite: {
plugins: [
tsConfigPaths({
projects: ['./tsconfig.json'],
}),
],
},
})
为了使 TanStack Start 正常运行,我们还需要在 ~/app/
中创建 5 个文件:
router.tsx
(路由器配置)ssr.tsx
(服务器入口点)client.tsx
(客户端入口点)routes/__root.tsx
(应用程序的根)routes/index.tsx
(主页)
您可以使用以下命令创建它们:
mkdir app
touch app/router.tsx
touch app/ssr.tsx
touch app/client.tsx
mkdir app/routes
touch app/routes/__root.tsx
touch app/routes/index.tsx
router.tsx
配置应用程序的主路由器,包括路由定义和设置。
import { createRouter as createTanStackRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'
export function createRouter() {
const router = createTanStackRouter({
routeTree,
scrollRestoration: true,
})
return router
}
declare module '@tanstack/react-router' {
interface Register {
router: ReturnType<typeof createRouter>
}
}
您应该会看到一个关于 routeTree.gen.ts
不存在的错误。这是预期的。它将在您第一次运行 TanStack Start 时生成。
ssr.tsx
允许我们知道当用户访问给定路由时需要执行哪些路由和加载器。
import {
createStartHandler,
defaultStreamHandler,
} from '@tanstack/react-start/server'
import { getRouterManifest } from '@tanstack/react-start/router-manifest'
import { createRouter } from './router'
export default createStartHandler({
createRouter,
getRouterManifest,
})(defaultStreamHandler)
client.tsx
初始化客户端逻辑以处理浏览器中的路由。
import { hydrateRoot } from "react-dom/client";
import { StartClient } from "@tanstack/react-start/client";
import { createRouter } from "./router";
const router = createRouter();
hydrateRoot(document, <StartClient router={router} />);
routes/__root.tsx
定义了整个应用程序的根路由和全局 HTML 布局。
import type { ReactNode } from "react";
import {
Outlet,
createRootRoute,
HeadContent,
Scripts,
} from "@tanstack/react-router";
export const Route = createRootRoute({
head: () => ({
meta: [
{
charSet: "utf-8",
},
{
name: "viewport",
content: "width=device-width, initial-scale=1",
},
{
title: "Prisma TanStack Start Demo",
},
],
}),
component: RootComponent,
});
function RootComponent() {
return (
<RootDocument>
<Outlet />
</RootDocument>
);
}
function RootDocument({ children }: Readonly<{ children: ReactNode }>) {
return (
<html>
<head>
<HeadContent />
</head>
<body>
{children}
<Scripts />
</body>
</html>
);
}
routes/index.tsx
是应用程序的主页。
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/")({
component: Home,
});
function Home() {
return (
<div>
<h1>Posts</h1>
</div>
);
}
现在,运行:
npm run dev
这将生成 routeTree.gen.ts
文件并解决所有路由错误。
您的文件树应如下所示(不包含 node_modules
):
.
├── app
│ ├── client.tsx
│ ├── routeTree.gen.ts
│ ├── router.tsx
│ ├── routes
│ │ ├── __root.tsx
│ │ └── index.tsx
│ └── ssr.tsx
├── app.config.ts
├── package-lock.json
├── package.json
└── tsconfig.json
2. 安装和配置 Prisma
2.1. 安装依赖项
要开始使用 Prisma,您需要安装一些依赖项:
- Prisma Postgres (推荐)
- 其他数据库
npm install prisma tsx --save-dev
npm install @prisma/extension-accelerate @prisma/client
npm install prisma tsx --save-dev
npm install @prisma/client
安装完成后,在您的项目中初始化 Prisma:
npx prisma init --db --output ../app/generated/prisma
您在设置 Prisma Postgres 数据库时需要回答几个问题。选择离您最近的区域,并为您的数据库取一个容易记住的名称,例如“我的 __________ 项目”。
这将创建:
- 一个包含
schema.prisma
文件的prisma
目录。 - 一个 Prisma Postgres 数据库。
- 项目根目录下的一个包含
DATABASE_URL
的.env
文件。 - 一个用于生成 Prisma Client 的
output
目录,路径为app/generated/prisma
。
2.2. 定义您的 Prisma Schema
在 schema.prisma
中,为我们的文章创建一个模型,并将生成器更改为使用 prisma-client
提供者:
generator client {
provider = "prisma-client"
output = "../app/generated/prisma"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
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)
authorId Int
author User @relation(fields: [authorId], references: [id])
}
这创建了两个模型:User
和 Post
,它们之间存在一对多关系。
2.3. 配置 Prisma Client 生成器
现在,运行以下命令来创建数据库表并生成 Prisma Client:
npx prisma migrate dev --name init
2.4. 填充数据库
让我们添加一些种子数据,用示例用户和文章来填充数据库。
在 prisma/
目录下创建一个名为 seed.ts
的新文件:
import { PrismaClient, Prisma } from "../src/generated/prisma/client.js";
const prisma = new PrismaClient();
const userData: Prisma.UserCreateInput[] = [
{
name: "Alice",
email: "alice@prisma.io",
posts: {
create: [
{
title: "Join the Prisma Discord",
content: "https://pris.ly/discord",
published: true,
},
{
title: "Prisma on YouTube",
content: "https://pris.ly/youtube",
},
],
},
},
{
name: "Bob",
email: "bob@prisma.io",
posts: {
create: [
{
title: "Follow Prisma on Twitter",
content: "https://www.twitter.com/prisma",
published: true,
},
],
},
},
];
export async function main() {
for (const u of userData) {
await prisma.user.create({ data: u });
}
}
main();
现在,通过更新您的 package.json
来告诉 Prisma 如何运行此脚本:
"prisma": {
"seed": "tsx prisma/seed.ts"
}
运行种子脚本:
npx prisma db seed
并打开 Prisma Studio 来检查您的数据:
npx prisma studio
3. 将 Prisma 集成到 TanStack Start
3.1 创建一个 Prisma Client
与其在每个文件中创建新的 Prisma Client 实例,不如在共享文件中创建一个单例实例以供全局使用。
创建一个 /lib
目录并在其中创建一个 prisma.ts
文件。该文件将用于创建和导出您的 Prisma Client 实例。
按如下方式设置 Prisma 客户端:
- Prisma Postgres (推荐)
- 其他数据库
import { PrismaClient } from "../generated/prisma/client.js";
import { withAccelerate } from "@prisma/extension-accelerate";
const prisma = new PrismaClient().$extends(withAccelerate());
export default prisma;
import { PrismaClient } from "../generated/prisma/client.js";
const prisma = new PrismaClient();
export default prisma;
我们建议使用连接池(如 Prisma Accelerate)来高效管理数据库连接。
如果您选择不使用连接池,请避免在长时间运行的环境中全局实例化 PrismaClient
。相反,应为每个请求创建和销毁客户端,以防止耗尽数据库连接。
3.2 在加载时获取用户和文章
首先,导入必要的模块。然后,使用 createServerFn
函数创建一个服务器函数。该函数将使用 .findMany()
方法从数据库中获取用户。使用 include
选项来获取相关的文章。
import { prisma } from "../lib/prisma";
import { createServerFn } from "@tanstack/react-start";
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/")({
component: Home,
});
const getUsers = createServerFn({ method: "GET" }).handler(async () => {
return prisma.user.findMany({
include: {
posts: true,
},
});
});
function Home() {
return (
<div>
<h1>Posts</h1>
</div>
);
}
TanStack Start 允许函数在加载时通过 createFileRoute
函数中的加载器函数运行。使用此代码在加载时获取用户及其文章:
import { prisma } from "../lib/prisma";
import { createServerFn } from "@tanstack/react-start";
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/")({
component: Home,
loader: () => {
return getUsers();
},
});
const getUsers = createServerFn({ method: "GET" }).handler(async () => {
return prisma.user.findMany({
include: {
posts: true,
},
});
});
function Home() {
return (
<div>
<h1>Posts</h1>
</div>
);
}
使用 Route.useLoaderData()
将加载器的响应存储在主组件中:
import { prisma } from "../lib/prisma";
import { createServerFn } from "@tanstack/react-start";
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/")({
component: Home,
loader: () => {
return getUsers();
},
});
const getUsers = createServerFn({ method: "GET" }).handler(async () => {
return prisma.user.findMany({
include: {
posts: true,
},
});
});
function Home() {
const users = Route.useLoaderData();
return (
<div>
<h1>Posts</h1>
</div>
);
}
3.3 显示用户和文章
接下来,您将更新主页以显示从数据库中检索到的用户和文章。
遍历 users
并将其与 posts
一起显示在列表中:
import { createFileRoute } from "@tanstack/react-router";
import { createServerFn } from "@tanstack/react-start";
import prisma from "../../lib/prisma";
export const Route = createFileRoute("/")({
component: Home,
loader: () => {
return getUsers();
},
});
const getUsers = createServerFn({ method: "GET" }).handler(async () => {
return prisma.user.findMany({
include: {
posts: true,
},
});
});
function Home() {
const users = Route.useLoaderData();
return (
<div>
<h1>Posts</h1>
<ul>
{users.map((user) => (
<li key={user.id}>
{user.name}
<ul>
{user.posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</li>
))}
</ul>
</div>
);
}
此设置将在您的页面上显示直接从数据库中获取的文章。
下一步
您已成功将 Prisma ORM 与 TanStack Start 集成,创建了一个无缝的全栈应用程序。以下是您可以接下来做的一些建议:
- 扩展您的 Prisma 模型以处理更复杂的数据关系。
- 实现额外的 CRUD 操作以增强应用程序的功能。
- 探索 Prisma 和 TanStack Start 的更多功能,以加深您的理解。
- 查看 Prisma Postgres,了解如何扩展您的应用程序。
更多信息
与 Prisma 保持联系
通过以下方式与 Prisma 保持联系,继续您的 Prisma 之旅: 我们的活跃社区。保持信息更新,参与其中,并与其他开发者协作
- 在 X 上关注我们 获取公告、实时活动和实用技巧。
- 加入我们的 Discord 提问、与社区交流,并通过对话获得积极支持。
- 在 YouTube 上订阅 观看教程、演示和直播。
- 在 GitHub 上参与 通过为仓库加星、报告问题或贡献问题。