自从我开始开发我的第一个项目(我的 OT Pokémon 和 Habbo 的第一个网站)以来,我一直选择 Raw SQL。老实说,我仍然非常喜欢编写自己的查询并更精确地控制这个“低级”层。 ORM 并不让我完全放心,因为我已经花了几天时间分析日志来识别和优化低效查询。
但是,在我使用 Raw SQL 的许多代码库中,绝大多数没有迁移控制,并且数据库也没有受到监控。一切都在即兴创作的基础上进行:“您需要一个新字段吗?运行 ALTER TABLE 并添加一个新列。”这种方法在所有场景下都是极其有害的,出现了几个问题,例如:“我们应该在生产环境中上哪些列?”,“创建了哪些新实体?”,“环境是否同步?” - 以及许多其他类似的问题。
我的问题的解决方案
面对所有这些问题,我决定采用新的工具来让我和与我一起工作的团队的日常工作变得更健康。我不想放弃我所拥有的灵活性,但我也想更好地控制应用程序的自由度。经过大量研究,我发现了一个我认为最完整的解决这些问题的工具:Kysely,它是 TypeScript 的查询构建器,除了实用之外,还是完全类型安全的 —对我来说非常重要的一点。这个库引起了我的注意,以至于我开始直接和间接地积极为社区做出贡献,为与 Kysely 集成的其他开源库创建插件。
然而,使用 Kysely 时最大的困难之一是,与 ORM 不同,它没有实体或自动生成类型/接口。所有这些工作都需要手动完成,这可能有点累人。在研究解决方案的过程中,我发现了一个最终在涉及 PostgreSQL 的所有项目中采用的工具:Kanel。 Kanel 自动生成数据库类型,完美补充了 Kysely。
此外,Kanel 还有一个可以直接与 Kysely 一起使用的附加功能:Kanel-Kysely。我一直在积极为这个存储库做出贡献,帮助开发新功能,例如迁移表的类型过滤器以及 Zod 对象到驼峰命名法的转换。
配置 Kysely
我将使用 NestJS 来说明以下示例。因此,如果您不理解某些语法或代码中的某些内容,我建议您阅读 NestJS 文档。在我看来,它是最好的 JavaScript 框架——特别是如果你想“逃避”JavaScript。但这是我另一篇文章的主题。
如果您想严格遵循示例,您需要先初始化一个带有 NestJS 的存储库。不过,您也可以开发自己的代码。
首先,我们需要安装 Kysely 本身、它的 CLI 和 Node.js 的 PostgreSQL 模块。
npm i kysely pg && npm i kysely-ctl --save-dev
接下来,我们需要在项目的根目录中为 Kysely 创建一个配置文件。我还将为我们的迁移和种子文件使用 Knex 前缀。
// kysely.config.ts import "dotenv/config"; import { defineConfig, getKnexTimestampPrefix } from "kysely-ctl"; import { Pool } from "pg"; export default defineConfig({ dialect: "pg", dialectConfig: { pool: new Pool({ connectionString: process.env.DATABASE_URL }), }, migrations: { migrationFolder: "src/database/migrations", getMigrationPrefix: getKnexTimestampPrefix, }, seeds: { seedFolder: "src/database/seeds", getSeedPrefix: getKnexTimestampPrefix, }, });
接下来,我们将在终端中运行命令 npx kysely migrate make create_user_table。它将负责创建我们的第一个迁移。接下来,我们将创建一个新的用户表,完成后,我们将使用命令 npx kysely migratelatest.
在数据库中运行此迁移
// 20241225222128_create_user_table.ts import { sql, type Kysely } from 'kysely' export async function up(db: Kysely<any>): Promise<void> { await db.schema .createTable("user") .addColumn("id", "serial", (col) => col.primaryKey()) .addColumn("name", "text", (col) => col.notNull()) .addColumn("email", "text", (col) => col.unique().notNull()) .addColumn("password", "text", (col) => col.notNull()) .addColumn("created_at", "timestamp", (col) => col.defaultTo(sql`now()`).notNull(), ) .execute(); } export async function down(db: Kysely<any>): Promise<void> { await db.schema.dropTable("user").execute(); } </void></any></void></any>
完成所有这些步骤后,让我们为我们的数据库创建一个模块。另请注意,我正在使用 Kysely 插件将我们的列转换为驼峰命名法。
// src/database/database.module.ts import { EnvService } from "@/env/env.service"; import { Global, Logger, Module } from "@nestjs/common"; import { CamelCasePlugin, Kysely, PostgresDialect } from "kysely"; import { Pool } from "pg"; export const DATABASE_CONNECTION = "DATABASE_CONNECTION"; @Global() @Module({ providers: [ { provide: DATABASE_CONNECTION, useFactory: async (envService: EnvService) => { const dialect = new PostgresDialect({ pool: new Pool({ connectionString: envService.get("DATABASE_URL"), }), }); const nodeEnv = envService.get("NODE_ENV"); const db = new Kysely({ dialect, plugins: [new CamelCasePlugin()], log: nodeEnv === "dev" ? ["query", "error"] : ["error"], }); const logger = new Logger("DatabaseModule"); logger.log("Successfully connected to database"); return db; }, inject: [EnvService], }, ], exports: [DATABASE_CONNECTION], }) export class DatabaseModule {}
配置 Kanel
让我们从安装依赖项开始。
npm i kanel kanel-kysely --save-dev
接下来,让我们为 Kanel 创建配置文件以开始工作。请注意,我将使用一些插件,例如camelCaseHook(将我们的接口转换为camelCase)和kyselyTypeFilter(以排除Kysely的迁移表),我很高兴能够贡献这些功能之一,并使我们的工作甚至更容易。
// .kanelrc.js require("dotenv/config"); const { kyselyCamelCaseHook, makeKyselyHook, kyselyTypeFilter } = require("kanel-kysely"); /** @type {import('kanel').Config} */ module.exports = { connection: { connectionString: process.env.DATABASE_URL, }, typeFilter: kyselyTypeFilter, preDeleteOutputFolder: true, outputPath: "./src/database/schema", preRenderHooks: [makeKyselyHook(), kyselyCamelCaseHook], };
创建文件后,我们将在终端中运行命令 npx kanel。请注意,在配置文件中指定的路径中创建了一个目录。此目录对应于您的架构的名称,在我们的示例中为 Public,其中我们有两个新文件:PublicSchema.ts 和 User.ts 。您的 User.ts 可能看起来完全像这样:
// @generated // This file is automatically generated by Kanel. Do not modify manually. import type { ColumnType, Selectable, Insertable, Updateable } from 'kysely'; /** Identifier type for public.user */ export type UserId = number & { __brand: 'UserId' }; /** Represents the table public.user */ export default interface UserTable { id: ColumnType<userid userid undefined>; name: ColumnType<string string>; email: ColumnType<string string>; password: ColumnType<string string>; createdAt: ColumnType<date date string undefined>; } export type User = Selectable<usertable>; export type NewUser = Insertable<usertable>; export type UserUpdate = Updateable<usertable>; </usertable></usertable></usertable></date></string></string></string></userid>
不过,最重要的是这个目录之外的文件Public,文件Database.ts,因为我们要传递的正是这个,以便Kysely能够理解我们数据库的整个结构。在我们的文件 app.service.ts 中,我们将注入 DatabaseModule 提供程序并将我们的类型 Database.
传递给 Kysely
npm i kysely pg && npm i kysely-ctl --save-dev
请注意,Kanel 生成的类型工作正常,因为我们的代码编辑器将准确建议我们在第一次迁移中创建的列。
最后的考虑因素
这是我非常喜欢在我的个人项目甚至工作中使用的组合(当我有自由这样做时)。对于喜欢原始 SQL 提供的灵活性但也选择“更安全”路径的每个人来说,查询生成器都是必不可少的工具。 Kanel 还为我节省了许多时间的调试和创建新类型的时间。我强烈建议你用这两个创建一个项目,你绝对不会后悔的。
存储库链接: Frankenstein-nodejs
以上是基斯利·卡内尔 (Kisley Kanel):完美二人组的详细内容。更多信息请关注PHP中文网其他相关文章!

JavaScript核心数据类型在浏览器和Node.js中一致,但处理方式和额外类型有所不同。1)全局对象在浏览器中为window,在Node.js中为global。2)Node.js独有Buffer对象,用于处理二进制数据。3)性能和时间处理在两者间也有差异,需根据环境调整代码。

JavaScriptusestwotypesofcomments:single-line(//)andmulti-line(//).1)Use//forquicknotesorsingle-lineexplanations.2)Use//forlongerexplanationsorcommentingoutblocksofcode.Commentsshouldexplainthe'why',notthe'what',andbeplacedabovetherelevantcodeforclari

Python和JavaScript的主要区别在于类型系统和应用场景。1.Python使用动态类型,适合科学计算和数据分析。2.JavaScript采用弱类型,广泛用于前端和全栈开发。两者在异步编程和性能优化上各有优势,选择时应根据项目需求决定。

选择Python还是JavaScript取决于项目类型:1)数据科学和自动化任务选择Python;2)前端和全栈开发选择JavaScript。Python因其在数据处理和自动化方面的强大库而备受青睐,而JavaScript则因其在网页交互和全栈开发中的优势而不可或缺。

Python和JavaScript各有优势,选择取决于项目需求和个人偏好。1.Python易学,语法简洁,适用于数据科学和后端开发,但执行速度较慢。2.JavaScript在前端开发中无处不在,异步编程能力强,Node.js使其适用于全栈开发,但语法可能复杂且易出错。

javascriptisnotbuiltoncorc; saninterpretedlanguagethatrunsonenginesoftenwritteninc.1)javascriptwasdesignedAsalightweight,解释edganguageforwebbrowsers.2)Enginesevolvedfromsimpleterterterpretpreterterterpretertestojitcompilerers,典型地提示。

JavaScript可用于前端和后端开发。前端通过DOM操作增强用户体验,后端通过Node.js处理服务器任务。1.前端示例:改变网页文本内容。2.后端示例:创建Node.js服务器。

选择Python还是JavaScript应基于职业发展、学习曲线和生态系统:1)职业发展:Python适合数据科学和后端开发,JavaScript适合前端和全栈开发。2)学习曲线:Python语法简洁,适合初学者;JavaScript语法灵活。3)生态系统:Python有丰富的科学计算库,JavaScript有强大的前端框架。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

SecLists
SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

适用于 Eclipse 的 SAP NetWeaver 服务器适配器
将Eclipse与SAP NetWeaver应用服务器集成。

DVWA
Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中

Dreamweaver CS6
视觉化网页开发工具