Postgres + Kysely 类型安全数据层实战:基于 Next.js Starter 构建可扩展的数据库应用
Postgres Kysely 类型安全数据层实战基于 Next.js Starter 构建可扩展的数据库应用【免费下载链接】examplesEnjoy our curated collection of examples and solutions. Use these patterns to build your own robust and scalable applications.项目地址: https://gitcode.com/GitHub_Trending/examples1/examples在 Next.js 应用中使用 PostgreSQL 数据库时如何在「免写手写 SQL」与「保持编译期类型安全」之间取得平衡是不少团队面临的现实问题。本文以仓库 storage/postgres-kysely 下的 Starter 模板为蓝本完整讲解如何用 Kysely一个 TypeScript 优先的 SQL 查询构建器连接 Postgres通过类型化 Schema 定义、链式建表、种子数据注入与服务端组件数据查询构建一个可直接部署、可随时扩展的完整示例。读完本文你将掌握这套 Starter 的两种启动方式、环境变量配置要点以及底层每个关键文件的设计意图能够直接复用它搭建自己的数据库驱动应用。项目概览一套最小但完整的数据库驱动应用该 Starter 的技术栈非常聚焦从 package.json 可以看出其核心依赖Next.jsApp Router 架构作为框架层Kyselykysely^0.26.3作为类型安全的 SQL 查询构建器kysely-postgres-jskysely-postgres-js^2.0.0作为 Kysely 与 postgres-js 驱动之间的方言适配层postgrespostgres^3.4.5作为底层 PostgreSQL 客户端驱动Tailwind CSS负责界面样式ms包用于相对时间格式化。整个应用围绕一张profiles用户表展开页面以「Recent Users」卡片列表的形式展示数据库中的用户数据并提供刷新按钮当数据表尚不存在时应用会自动建表并注入三条示例数据。README 顶部 front matter 中标明其使用场景为useCase: Starter、database: Postgres与仓库中postgres-starter、postgres-prisma、postgres-sveltekit等模板互为对照见 README.md是理解同一主题下不同数据访问方案的理想参照物。快速开始两种方式启动项目README 提供了两条完全等效的启动路径任选其一即可。方式一一键部署在 README 页面点击「Deploy with Vercel」按钮即可将整个仓库克隆到 Vercel 平台完成部署同时会引导你创建配套的 Postgres 数据库实例并把数据库连接信息注入到环境变量中。这种方式适合快速验证效果、或者只是想把它当作线上 Demo 使用的场景。方式二Clone 后本地运行先用 pnpm 借助create-next-app拉取模板使用该命令前请先安装 pnpmpnpm create next-app --example https://github.com/vercel/examples/tree/main/storage/postgres-kysely拉取完成后把模板目录中的.env.example复制为本地环境变量文件.env.local该文件已被 Git 忽略不会提交到仓库cp .env.example .env.local然后打开.env.local将里面的环境变量值替换为你在 Vercel Storage Dashboard 中创建数据库后得到的实际连接信息。接着以开发模式启动 Next.jspnpm dev应用默认运行在本机开发端口浏览器访问后即可看到从 Postgres 实时查询出的用户列表。环境变量POSTGRES_URL 的连接语义模板通过单个环境变量POSTGRES_URL承载完整的 PostgreSQL 连接字符串包含用户名、密码、主机、端口、数据库名等全部信息。在 lib/kysely.ts 中连接池的创建代码如下export const db new KyselyDatabase({ dialect: new PostgresJSDialect({ postgres: postgres(process.env.POSTGRES_URL!, { ssl: require }), }), })这里有两个值得注意的细节process.env.POSTGRES_URL!非空断言表明该环境变量是运行时的硬性前提。若未配置应用启动后执行数据库操作会直接报错因此部署前务必确认.env.local本地或 Vercel 平台环境变量线上中已正确设置{ ssl: require }强制要求 TLS 加密连接这是连接云端托管 Postgres如 Vercel Storage的标准做法。本地若连接的是不带 SSL 的开发库需要按实际情况调整此参数。从源码结构可以推断该模板刻意保持「单环境变量、零配置文件」的最小设计——连接逻辑、Schema 定义、查询执行全部收敛在这一个模块中便于后续扩展。类型安全的核心数据库 Schema 的编译期建模Kysely 的核心价值在于把数据库表结构建模为 TypeScript 类型让 SQL 拼写错误在编译期就被拦截。lib/kysely.ts 用接口描述了profiles表interface ProfileTable { // 数据库自动生成的列使用 Generated 类型标记 // 使该字段在 insert/update 时自动变为可选 id: Generatednumber name: string email: string image: string // 通过 ColumnTypeSelectType, InsertType, UpdateType 泛型 // 可以为 select、insert、update 三种操作分别指定不同类型。 // 这里 createdAt 在查询时是 Date插入时可选地接受 string // 且永远不允许在 update 时被修改 createdAt: ColumnTypeDate, string | undefined, never } // 该接口的键即为表名 export interface Database { profiles: ProfileTable }这两个类型工具是理解 Kysely 类型模型的关键GeneratedT标记由数据库生成的列如自增主键、时间戳默认值。Kysely 会自动让这类列在 insert/update 语句中变为可选避免开发者误写入被数据库接管的值ColumnTypeSelect, Insert, Update允许针对读写不同场景声明差异化类型。示例中createdAt查询返回Date、插入接受string | undefined、更新被声明为never即禁止在 update 中触碰该列从类型层面直接杜绝了误更新时间戳这类常见错误。文件末尾的export { sql } from kysely则暴露了原始 SQL 片段工具供种子脚本等处书写数据库原生表达式如current_timestamp、timestamp with time zone使用。建表与数据初始化seed 脚本逐段解析lib/seed.ts 承担「建表 灌入示例数据」双重职责其中表结构的创建使用了 Kysely 的链式 Schema Builder APIconst createTable await db.schema .createTable(profiles) .ifNotExists() .addColumn(id, serial, (cb) cb.primaryKey()) .addColumn(name, varchar(255), (cb) cb.notNull()) .addColumn(email, varchar(255), (cb) cb.notNull().unique()) .addColumn(image, varchar(255)) .addColumn(createdAt, sqltimestamp with time zone, (cb) cb.defaultTo(sqlcurrent_timestamp) ) .execute()各列定义说明如下列名类型约束/默认值语义idserial主键数据库自增 IDnamevarchar(255)非空用户显示名emailvarchar(255)非空且唯一用户邮箱用作业务唯一键imagevarchar(255)无头像图片 URLcreatedAttimestamp with time zonecurrent_timestamp创建时间写入时自动填充注意建表采用了.ifNotExists()保证幂等性多次执行不会报错email的.unique()约束与下方种子数据的ON CONFLICT (email) DO NOTHING相互配合确保重复执行 seed 也不会产生重复记录。种子数据通过三条独立的sql模板语句插入对应 Guillermo Rauch、Lee Robinson、Steven Tey 三位示例用户每次插入都携带ON CONFLICT (email) DO NOTHING子句最后统一返回{ createTable, addUsers }便于调用方拿到执行结果。数据读取链路从服务端组件到自动建表应用没有独立的初始化脚本而是把建表与查询的联动逻辑内聚在服务端组件 components/table.tsx 中这也是整个模板最值得借鉴的「懒初始化」模式export default async function Table() { let users let startTime Date.now() try { users await db.selectFrom(profiles).selectAll().execute() } catch (e: any) { if (e.message relation profiles does not exist) { // 表尚未创建先建表并注入示例数据再重新查询 await seed() users await db.selectFrom(profiles).selectAll().execute() } else { throw e } } // ... }这条链路包含三层关键设计查询构建db.selectFrom(profiles).selectAll().execute()是 Kysely 的典型链式 API得益于Database类型profiles表名、返回字段都会被严格校验容错重试捕获relation profiles does not exist这一特定错误对应 PostgreSQL 中「表不存在」的标准报错信息触发seed()完成建表与数据注入后再次查询实现「首次访问自动初始化」请求耗时统计通过Date.now()计算查询耗时并展示在界面上Fetched {users.length} users in {duration}ms便于直观验证数据库往返性能。页面组件 app/page.tsx 还声明了两个影响渲染行为的导出export const preferredRegion home export const dynamic force-dynamic其中force-dynamic强制每次请求都走服务端实时渲染避免静态缓存导致数据过期preferredRegion则把函数部署到home区域这对依赖数据库实时状态的页面是必要的配置。加载占位与无刷新更新完善的前端体验围绕数据渲染模板还提供了两个配套组件components/table-placeholder.tsx与真实表格同构的骨架屏通过animate-pulse动画模拟三行用户数据的加载状态。它在 app/page.tsx 中作为Suspense的 fallback 使用让用户感知到「数据正在获取」而不是白屏components/refresh-button.tsx一个客户端组件调用 Next.js App Router 的router.refresh()在不刷新整个页面的前提下重新执行服务端组件、拉取最新数据配合useTransition在刷新期间禁用按钮并显示 Refreshing... 文案。这两个组件共同构成了「首屏骨架屏 增量数据刷新」的完整交互闭环也是 App Router 流式渲染与 RSC 能力的一个精炼演示。运行、构建与部署README 末尾给出了完整的运行与上线闭环。本地开发使用pnpm dev验证生产构建与启动则可使用 package.json 中预置的脚本pnpm build pnpm start部署时同样借助 Vercel 平台在pnpm create next-app拉取的模板目录上直接关联 Git 仓库并导入 Vercel平台会自动识别 Next.js 项目、安装依赖并执行构建你只需在项目设置中补充POSTGRES_URL环境变量值来自 Vercel Storage Dashboard。另外next.config.js 中为 Next.jsImage组件配置了images.ctfassets.net白名单因为种子数据中的头像图片托管在该域名下若替换为自己的图片源需同步更新此配置。项目结构速览将 storage/postgres-kysely 目录展开核心文件及其职责如下storage/postgres-kysely/ ├── app/ │ ├── layout.tsx # 根布局定义全局元信息与字体 │ ├── page.tsx # 首页声明 force-dynamic组装数据卡片 │ ├── globals.css # 全局样式Tailwind │ └── opengraph-image.png # 演示用 OpenGraph 截图 ├── components/ │ ├── table.tsx # 服务端组件查询、自动建表、渲染用户列表 │ ├── table-placeholder.tsx # Suspense 骨架屏 │ ├── refresh-button.tsx # router.refresh() 无刷新更新按钮 │ └── expanding-arrow.tsx # 装饰性箭头图标 ├── lib/ │ ├── kysely.ts # Kysely 实例 类型化 Schema 定义 │ ├── seed.ts # 建表与种子数据注入 │ └── utils.ts # timeAgo 相对时间格式化 ├── public/ # 静态资源logo 等 ├── .env.example # 环境变量模板POSTGRES_URL ├── next.config.js # 图片域名白名单等配置 ├── package.json # 依赖与脚本 └── tsconfig.json # TypeScript 配置含 /* 路径别名小结postgres-kyselyStarter 的价值在于它用最少的代码把「类型安全的查询构建器 Next.js App Router 服务端组件 云端 Postgres」三者有机串联lib/kysely.ts负责类型化建模与连接lib/seed.ts负责幂等建表与灌数据components/table.tsx实现查询与懒初始化配合骨架屏与router.refresh()完善交互体验。对于希望在不引入重量级 ORM 的前提下获得编译期数据库类型检查的团队这套模式可以直接作为业务起点——替换表结构、调整Database类型与查询逻辑即可快速演进为生产级应用。仓库中同目录下的 postgres-starter、postgres-drizzle 等模板则提供了同一场景下的其他方案对比可按需参考。【免费下载链接】examplesEnjoy our curated collection of examples and solutions. Use these patterns to build your own robust and scalable applications.项目地址: https://gitcode.com/GitHub_Trending/examples1/examples创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考