create-t3-app 新项目启动指南:数据库初始化与 Discord 登录配置(First Steps 全解析)
创建完一个 create-t3-app 项目之后,真正的开发才刚刚开始。这篇指南以官方文档 fr/usage/first-steps.md(及对应的英文版 en/usage/first-steps.md)为骨架,结合仓库内 CLI 的实际安装器源码,讲清楚让新生成的 T3 应用真正跑起来的"最小必要步骤":数据库的初始化与推模式(Prisma / Drizzle)、NextAuth.js 的 Discord 登录配置,以及如何从示例代码理解 tRPC 查询。读完你就能独立把脚手架项目启动到可登录、可读写数据的状态。
概览:脚手架生成后你需要做什么
当你通过 pnpm create t3-app@latest(或 npm / yarn / bun 等价命令)完成交互式选型后,CLI 已经替你完成了依赖安装、目录脚手架和基础配置。但有两类事情是 CLI 无法替你完成的:
- 数据库本身:CLI 只写入了
DATABASE_URL等环境变量和 schema,并不会替你创建数据库实例; - 第三方 OAuth 应用:Discord 的 Client ID / Secret 必须由你在 Discord 开发者后台申请,并回填到
.env。
CLI 在项目生成结束时,会通过 logNextSteps.ts 在终端打印后续步骤提示,其逻辑恰好对应本文的三大部分:启动数据库(./start-database.sh)、推送 schema(db:push)、填充 .env 认证变量(Fill in your .env with necessary values)。
数据库:三种情况的处理方式
脚手架根据你的选型,会生成不同的数据库工具链。官方文档将其拆为三类场景。
MySQL / PostgreSQL:使用 start-database.sh 一键拉起容器
如果你在 CLI 交互中选择了 MySQL 或 PostgreSQL,生成的工程根部会带有一个 start-database.sh bash 脚本。它的作用是用 Docker(或 Podman)启动一个本地开发数据库容器,避免你手动安装数据库服务。
仓库中的模板脚本 postgres.sh 展示了它的实现思路:
- 从
.env中解析DATABASE_URL,用awk提取出密码(DB_PASSWORD)、端口(DB_PORT)和库名(DB_NAME); - 优先使用
docker,不可用时回退到podman; - 检查端口是否被占用(依赖
nc/netcat); - 若容器已存在则直接启动,否则执行
docker run,以-p "$DB_PORT":5432映射端口并传入POSTGRES_USER、POSTGRES_PASSWORD、POSTGRES_DB三个环境变量; - 如果检测到密码仍是默认的
password,会提示你是否用openssl rand -base64 12生成随机密码并回写.env。
对应地,CLI 安装器在 dbContainer.ts 阶段为 postgres/mysql 两种 provider 复制该脚本,并在 logNextSteps.ts 中提示你 Start up a database, if needed using './start-database.sh'。
如果你已经有一个现成的数据库,官方文档的建议是:直接删除这个脚本文件,把真实连接串填进 .env 的 DATABASE_URL 即可。macOS 用户若不想用 Docker,也可以使用 DBngin 这类数据库管理工具。
Prisma:npx prisma db push 同步 schema
如果工程包含 Prisma,请从工程根目录执行:
npx prisma db push
这条命令做两件事:
- 把
prisma/schema.prisma中的模型定义同步到数据库(创建/更新表结构); - 根据 schema 重新生成 Prisma Client 的 TypeScript 类型。
其中第 2 步对编辑器体验很关键:生成类型后需要重启 TypeScript 服务(VS Code 中通过命令面板执行 "TypeScript: Restart TS Server"),否则编辑器无法识别新生成的 Prisma Client 类型,会报大量类型错误。
仓库中的 Prisma 安装器 prisma.ts 印证了这套工作流:它会向 package.json 注入 db:push(prisma db push)、db:studio(prisma studio)、db:migrate(prisma migrate deploy)、db:generate(prisma migrate dev)以及 postinstall: prisma generate 等脚本。也就是说,你也可以用包管理器等价命令:
pnpm db:push # 或 npm run db:push / yarn db:push / bun run db:push
安装器还会根据你的选型复制不同版本的 schema:base.prisma 只有 Post 模型;若启用了 NextAuth,则换成 with-auth.prisma,额外包含 Account、Session、User、VerificationToken 四个 NextAuth 适配器必需的模型。同时,schema 中的 provider 会按所选数据库改写为 sqlite / mysql / postgresql,MySQL 与 PlanetScale 场景还会自动取消 @db.Text 注释。
Drizzle:db:push 推送 schema
如果选择 Drizzle ORM,官方文档的指引是:先查看 .env 中关于如何构造 DATABASE_URL 的注释说明,填好环境变量后运行:
pnpm db:push
Drizzle 安装器 drizzle.ts 为工程注入的脚本包括:db:push(drizzle-kit push)、db:studio(drizzle-kit studio)、db:generate(drizzle-kit generate)、db:migrate(drizzle-kit migrate)。生成的配置模板 drizzle-config-postgres.ts 演示了 drizzle.config.ts 的标准形态:
import { type Config } from "drizzle-kit";
import { env } from "~/env";
export default {
schema: "./src/server/db/schema.ts",
dialect: "postgresql",
dbCredentials: {
url: env.DATABASE_URL,
},
tablesFilter: ["project1_*"],
} satisfies Config;
注意其中的 tablesFilter:安装器会把 project1_* 占位符替换为你的项目名,用来限制 drizzle-kit 只管理属于本应用的表,避免误伤同库中的其他表。
环境变量与 .env 的生成规则
无论选择哪条数据库路线,.env 都是关键。CLI 的 envVars.ts 会按选型生成不同的 .env / .env.example 内容:
- sqlite:
DATABASE_URL="file:./db.sqlite" - mysql:
DATABASE_URL="mysql://root:password@localhost:3306/<appName>" - postgres:
DATABASE_URL="postgresql://postgres:password@localhost:5432/<appName>" - planetscale(Prisma):
DATABASE_URL='mysql://YOUR_MYSQL_URL_HERE?sslaccept=strict' - planetscale(Drizzle):
DATABASE_URL='mysql://YOUR_MYSQL_URL_HERE?ssl={"rejectUnauthorized":true}'
同时,envVars.ts 会为认证功能生成随机的 AUTH_SECRET(32 字节随机数的 base64)写入 .env,但不会写入 .env.example——因为后者会提交到版本库,绝不能包含密钥。
认证:配置 NextAuth.js 的 DiscordProvider
如果工程包含 NextAuth.js,脚手架默认用 DiscordProvider 帮你起步。之所以选 Discord,是因为它是 NextAuth 提供的最简单的 OAuth 提供方之一,但即便如此,仍需要你手动完成少量初始配置。
为什么默认是 Discord
在 auth/config/base.ts 中可以看到,生成的认证配置直接引入了 DiscordProvider:
export const authConfig = {
providers: [
DiscordProvider,
/**
* ...add more providers here.
*
* Most other providers require a bit more work than the Discord provider.
* For example, the GitHub provider requires you to add the
* `refresh_token_expires_in` field to the Account model.
*/
],
callbacks: {
session: ({ session, token }) => ({
...session,
user: { ...session.user, id: token.sub },
}),
},
} satisfies NextAuthConfig;
注释里明确写着:其他大多数 provider 比 Discord 需要更多工作(比如 GitHub provider 要求在 Prisma 的 Account 模型上补充 refresh_token_expires_in 字段)。这正是"先从 Discord 起步"的工程考量。如果你偏好其他提供方,可以直接替换 providers 数组并到 NextAuth 官方 providers 列表中选择。
Discord 后台配置步骤
- 准备一个 Discord 账号(没有就先注册);
- 打开 https://discord.com/developers/applications,点击右上角 "New Application",为应用命名并同意服务条款;
- 创建成功后,进入 "Settings → OAuth2 → General";
- 复制 "Client ID",填入
.env的AUTH_DISCORD_ID; - 点击 "Reset Secret",复制新生成的 secret,填入
.env的AUTH_DISCORD_SECRET(注意官方文档法文版中误写的DISCORD CLIENT_SECRET实为AUTH_DISCORD_SECRET,英文版与 envVars.ts 源码均以AUTH_DISCORD_SECRET为准); - 点击 "Add Redirect",填入
http://localhost:3000/api/auth/callback/discord:- 生产部署时:按同样步骤另建一个 Discord Application,并把
http://localhost:3000替换为你实际部署的域名;
- 生产部署时:按同样步骤另建一个 Discord Application,并把
- 保存更改。
完成后运行 pnpm dev(或 npm run dev),应用即可通过 Discord 登录。
生成的 .env 与类型安全校验
启用 NextAuth 后,CLI 会在 .env 中生成以下认证相关变量(见 envVars.ts):
# Next Auth
# You can generate a new secret on the command line with:
# npx auth secret
AUTH_SECRET=""
# Next Auth Discord Provider
AUTH_DISCORD_ID=""
AUTH_DISCORD_SECRET=""
同时,envVars.ts 已经为 AUTH_SECRET 生成了一串随机值,并注释 # Generated by create-t3-app.。
这些变量并非"填了就行"——它们还会被 with-auth-db.js 中的 createEnv 严格校验:AUTH_SECRET 在生产环境必须为非空字符串(开发环境可选)、AUTH_DISCORD_ID 与 AUTH_DISCORD_SECRET 必须是 z.string()、DATABASE_URL 必须是合法 URL。缺失或格式错误时,next dev / next build 会直接抛出类型校验错误,这就是 T3 应用"类型安全环境变量"的体现。若你需要在 Docker 等无 env 场景下跳过校验,可以设置 SKIP_ENV_VALIDATION 环境变量(见 with-auth-db.js)。
Prisma + NextAuth 的模型联动
若同时选择了 Prisma 与 NextAuth,生成的 schema 会包含四个认证模型:Account、Session、User、VerificationToken(见 with-auth.prisma)。其中 Post 模型还会新增 createdBy / createdById 字段,与 User 建立一对多关系。因此必须先执行 npx prisma db push 再启动登录流程,否则 NextAuth 写入 session 时会因表不存在而报错——这也解释了为什么官方文档把"数据库"排在"认证"之前。
下一步:从示例理解 tRPC 查询
登录与数据库就绪后,就可以开始写业务代码了。若工程包含 tRPC,官方文档建议你重点看两个文件:
src/pages/index.tsx(Pages Router 场景)或src/app/page.tsx(App Router 场景)——页面如何调用 tRPC;src/server/api/routers/post.ts——服务端路由如何定义。
仓库模板中的示例路由 post/base.ts 是理解 tRPC 用法的最小样例:
export const postRouter = createTRPCRouter({
hello: publicProcedure
.input(z.object({ text: z.string() }))
.query(({ input }) => ({ greeting: `Hello ${input.text}` })),
create: publicProcedure
.input(z.object({ name: z.string().min(1) }))
.mutation(async ({ input }) => { /* ... */ }),
getLatest: publicProcedure.query(() => posts.at(-1) ?? null),
});
它展示了 tRPC 的三个核心概念:
createTRPCRouter:把一组 procedure 组合成一个路由对象;publicProcedure.query/.mutation:区分只读查询与写入操作;.input(z.object(...)):用 Zod 定义输入校验,实现端到端类型安全——客户端调用时的类型推断、运行时校验都由此驱动。
对照文件树,你会发现模板为不同选型准备了多套路由版本:post/base.ts、post/with-auth.ts、post/with-prisma.ts、post/with-drizzle.ts 等(见 post 路由目录),CLI 会根据是否启用认证/数据库复制对应版本。同理,页面模板(如 with-auth-trpc-tw.tsx)会按组合选型生成,这就是为什么不同选型的项目首页展示内容不同。
小结
让一个新建的 create-t3-app 工程跑起来,只需按顺序完成三件事:
- 数据库:MySQL/PostgreSQL 用
./start-database.sh起容器,或直接复用已有库并在.env填好DATABASE_URL;Prisma 执行npx prisma db push,Drizzle 执行pnpm db:push(记得重启 TypeScript 服务); - 认证:在 Discord 开发者后台创建应用,把
AUTH_DISCORD_ID、AUTH_DISCORD_SECRET填入.env,并配置回跳地址http://localhost:3000/api/auth/callback/discord(生产环境换成部署域名); - 开发:运行
pnpm dev启动应用,通过 post/base.ts 等示例理解 tRPC 的 router / procedure / input 三段式写法。
关于脚手架生成时每一步具体做了什么,你可以在仓库的 installers 目录 找到全部安装器源码;CLI 打印的"Next steps"提示逻辑则见 logNextSteps.ts,它与你看到的终端输出一一对应。
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/gitblog_00472/article/details/156482450



