如何在 TanStack Start 中使用 Prisma ORM
介绍
¥Introduction
Prisma ORM 简化了数据库交互,TanStack Start 为构建现代 React 应用提供了一个强大的框架。它们与 Prisma Postgres 一起,提供无缝的全栈开发体验,具有类型安全的查询和高效的数据管理。
¥Prisma ORM simplifies database interactions, and TanStack Start offers a robust framework for building modern React applications. Together with Prisma Postgres, they provide a seamless full-stack development experience with type-safe queries and efficient data management.
本指南将指导你从头开始在 TanStack Start 项目中将 Prisma ORM 与 Prisma Postgres 数据库集成。
¥This guide will walk you through integrating Prisma ORM with a Prisma Postgres database in a TanStack Start project from scratch.
先决条件
¥Prerequisites
1. 设置你的项目
¥ Set up your project
首先,创建一个新的 TanStack Start 项目。
¥To begin, create a new TanStack Start project.
在本指南中,我们使用的设置说明与你在 TanStart 启动文档 中找到的相同。
¥For the purpose of this guide, we're using the same setup instructions that you can find in the TanStart Start docs.
在你想要创建项目的目录中,运行以下命令:
¥In the directory where you'd like to create your project, run the following commands:
mkdir tanstack-start-prisma
cd tanstack-start-prisma
npm init -y
这将创建一个名为 tanstack-start-prisma 的新文件夹,导航到该文件夹,并初始化一个新的 Node.js 项目。
¥This will create a new folder called tanstack-start-prisma, navigate into it, and initialize a new Node.js project.
在 IDE 中打开目录并创建一个 tsconfig.json 文件,配置如下:
¥Open the directory in your IDE and create a tsconfig.json file with the following configuration:
{
"compilerOptions": {
"jsx": "react-jsx",
"moduleResolution": "Bundler",
"module": "ESNext",
"target": "ES2022",
"skipLibCheck": true,
"strictNullChecks": true
}
}
我们还需要一个 .gitignore 文件,现在就开始设置:
¥We also need a .gitignore file, so let's set that up now:
node_modules
.env
app/generated
接下来,安装 TanStack Router 和 Vinxi,因为 TanStack Start 目前需要它们:
¥Next, install TanStack Router and Vinxi, as TanStack Start currently requires them:
npm install @tanstack/react-start @tanstack/react-router vinxi
我们还需要 React、Vite React 插件和 TypeScript:
¥We also need React, the Vite React plugin, and 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:
¥Update your package.json to use Vinxi's CLI. Add "type": "module" and modify the scripts to use Vinxi's 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 文件:
¥Then, create and configure TanStack Start's app.config.ts file:
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 个文件:
¥For TanStack Start to function, we need 5 files in ~/app/:
-
router.tsx(路由配置)¥
router.tsx(The router configuration) -
ssr.tsx(服务器入口点)¥
ssr.tsx(The server entry point) -
client.tsx(客户端入口点)¥
client.tsx(The client entry point) -
routes/__root.tsx(应用的根目录)¥
routes/__root.tsx(The root of the app) -
routes/index.tsx(主页)¥
routes/index.tsx(The home page)
你可以使用以下命令创建它们:
¥You can create them with these commands:
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 使用路由定义和设置配置应用的主路由:
¥router.tsx configures the application's main router with route definitions and settings:
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 时生成。
¥You should be seeing an error about routeTree.gen.ts not existing. This is expected. It will be generated when you run TanStack Start for the first time.
ssr.tsx 让我们知道当用户点击指定路由时需要执行哪些路由和加载器:
¥ssr.tsx allows us to know what routes and loaders we need to execute when the user hits a given route:
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 初始化客户端逻辑以处理浏览器中的路由:
¥client.tsx initializes the client-side logic to handle routes in the browser:
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 布局:
¥routes/__root.tsx defines the root route and global HTML layout for the entire application:
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 是应用的主页:
¥routes/index.tsx is the home page of the application:
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/")({
component: Home,
});
function Home() {
return (
<div>
<h1>Posts</h1>
</div>
);
}
现在,运行:
¥Now, run:
npm run dev
这将生成 routeTree.gen.ts 文件并解决任何路由错误。
¥This will generate the routeTree.gen.ts file and resolve any routing errors.
你的文件树应如下所示(不包含 node_modules):
¥Your file tree should look like this (without 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
¥ Install and Configure Prisma
2.1.安装依赖
¥2.1. Install dependencies
要开始使用 Prisma,你需要安装一些依赖:
¥To get started with Prisma, you'll need to install a few dependencies:
- Prisma Postgres (recommended)
- Other databases
npm install prisma tsx --save-dev
npm install @prisma/extension-accelerate @prisma/client dotenv
npm install prisma tsx --save-dev
npm install @prisma/client dotenv
安装完成后,请在你的项目中初始化 Prisma:
¥Once installed, initialize Prisma in your project:
npx prisma init --db --output ../app/generated/prisma
在设置 Prisma Postgres 数据库时,你需要回答几个问题。选择离你最近的区域,并为数据库选择一个容易记住的名称,例如 "我的 __________ 项目"
¥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 __________ Project"
这将造成:
¥This will create:
-
一个包含
schema.prisma文件的prisma目录。¥A
prismadirectory with aschema.prismafile. -
A
prisma.config.tsfile for configuring Prisma -
一个 Prisma Postgres 数据库。
¥A Prisma Postgres database.
-
一个位于项目根目录、包含
DATABASE_URL文件的.env文件。¥A
.envfile containing theDATABASE_URLat the project root. -
用于生成的 Prisma 客户端的
output目录,用作app/generated/prisma。¥An
outputdirectory for the generated Prisma Client asapp/generated/prisma.
2.2.定义 Prisma Schema
¥2.2. Define your Prisma Schema
在 schema.prisma 中,为我们的帖子创建一个模型,并将生成器更改为使用 prisma-client 提供程序:
¥In schema.prisma, create a model for our posts and change the generator to use the prisma-client provider:
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,它们之间存在一对多关系。
¥This creates two models: User and Post, with a one-to-many relationship between them.
2.3 Add dotenv to prisma.config.ts
To get access to the variables in the .env file, they can either be loaded by your runtime, or by using dotenv.Include an import for dotenv at the top of the prisma.config.ts
import 'dotenv/config'
import { defineConfig, env } from 'prisma/config';
export default defineConfig({
schema: 'prisma/schema.prisma',
migrations: {
path: 'prisma/migrations',
},
engine: 'classic',
datasource: {
url: env('DATABASE_URL'),
},
});
2.4.配置 Prisma 客户端生成器
¥2.4. Configure the Prisma Client generator
现在,运行以下命令创建数据库表并生成 Prisma 客户端:
¥Now, run the following command to create the database tables and generate the Prisma Client:
npx prisma migrate dev --name init
2.5.种子数据库
¥2.5. Seed the database
让我们添加一些种子数据,以便用示例用户和帖子填充数据库。
¥Let's add some seed data to populate the database with sample users and posts.
在 prisma/ 目录中创建一个名为 seed.ts 的新文件:
¥Create a new file called seed.ts in the prisma/ directory:
import { PrismaClient, Prisma } from "../app/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();
现在,通过更新 prisma.config.ts 来告诉 Prisma 如何运行此脚本:
¥Now, tell Prisma how to run this script by updating your prisma.config.ts:
import 'dotenv/config'
import { defineConfig, env } from 'prisma/config';
export default defineConfig({
schema: 'prisma/schema.prisma',
migrations: {
path: 'prisma/migrations',
seed: `tsx prisma/seed.ts`,
},
engine: 'classic',
datasource: {
url: env('DATABASE_URL'),
},
});
运行种子脚本:
¥Run the seed script:
npx prisma db seed
打开 Prisma Studio 检查你的数据:
¥And open Prisma Studio to inspect your data:
npx prisma studio
3. 将 Prisma 集成到 TanStack Start
¥ Integrate Prisma into TanStack Start
3.1 创建 Prisma 客户端
¥3.1 Create a Prisma Client
无需在每个文件中创建新的 Prisma 客户端实例,而是在共享文件中创建一个全局使用的实例。
¥Instead of creating a new Prisma Client instance in each file, create a single instance in a shared file to be used globally.
在其中创建一个 /lib 目录和一个 prisma.ts 文件。此文件将用于创建和导出你的 Prisma 客户端实例。
¥Create a /lib directory and a prisma.ts file inside it. This file will be used to create and export your Prisma Client instance.
按如下方式设置 Prisma 客户端:
¥Set up the Prisma client like this:
- Prisma Postgres (recommended)
- Other databases
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 加速)来高效管理数据库连接。
¥We recommend using a connection pooler (like Prisma Accelerate) to manage database connections efficiently.
如果你选择不使用 PrismaClient,请避免在长期存在的环境中全局实例化 PrismaClient。请改为按请求创建并释放客户端,以防止耗尽数据库连接。
¥If you choose not to use one, avoid instantiating PrismaClient globally in long-lived environments. Instead, create and dispose of the client per request to prevent exhausting your database connections.
3.2 加载时获取用户和帖子
¥3.2 Fetch users and posts on load
首先,导入必要的模块。然后,使用 createServerFn 函数创建一个服务器函数。此函数将使用 .findMany() 方法从数据库中获取用户。使用 include 选项获取相关文章:
¥First, import the necessary modules. Then, create a server function using the createServerFn function. This function will fetch the users from the database using the .findMany() method. Use the include option to fetch the related posts:
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 函数中使用加载器函数在负载下运行。使用以下代码在加载时获取用户及其帖子:
¥TanStack Start allows functions to run on load with loader functions in the createFileRoute function. Fetch the users and their posts on load with this code:
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() 将加载器的响应存储在主组件中:
¥Store the response from the loader in the main component using 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 显示用户和帖子
¥3.3 Display the users and posts
接下来,你将更新主页以显示从数据库检索到的用户和帖子。
¥Next, you'll update the home page to display the users and posts retrieved from your database.
映射 users 并将它们与 posts 一起显示在列表中:
¥Map over the users and display them in a list along with their 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>
);
}
此设置将在你的页面上显示直接从数据库获取的帖子。
¥This setup will display the posts on your page, fetched directly from your database.
下一步
¥Next steps
你已成功将 Prisma ORM 与 TanStack Start 集成,从而创建了一个无缝的全栈应用。以下是一些关于你接下来可以做什么的建议:
¥You've successfully integrated Prisma ORM with TanStack Start, creating a seamless full-stack application. Here are a few suggestions for what you can do next:
-
扩展你的 Prisma 模型以处理更复杂的数据关系。
¥Expand your Prisma models to handle more complex data relationships.
-
实现额外的 CRUD 操作以增强应用的功能。
¥Implement additional CRUD operations to enhance your application's functionality.
-
探索 Prisma 和 TanStack 的更多功能,加深你的理解。
¥Explore more features of Prisma and TanStack Start to deepen your understanding.
-
查看 Prisma Postgres,了解如何扩展你的应用。
¥Check out Prisma Postgres to see how you can scale your application.
更多信息
¥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:
- Follow us on X for announcements, live events and useful tips.
- Join our Discord to ask questions, talk to the community, and get active support through conversations.
- Subscribe on YouTube for tutorials, demos, and streams.
- Engage on GitHub by starring the repository, reporting issues, or contributing to an issue.