如何在 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 上參與 透過為倉庫加星、報告問題或貢獻問題。