我如何在 Node.js 中构建多租户 SaaS 产品
几个月前,我不得不使用 Node.js 构建一个基于 URL 的多租户 SaaS 产品。我不知道它是什么。所以我在 Google 上搜索了“什么是多租户架构”、“如何在 Node.js 中构建多租户架构”等等。不幸的是,我找不到很多有用的内容,但最后,我发现了 Knex(发音为 /kəˈnɛks/),它是一个强大的 SQL 查询构建器。我想借此机会分享一些我希望一些人会发现相关和有用的东西。 多租
几个月前,我不得不使用 Node.js 构建一个基于 URL 的多租户 SaaS 产品。我不知道它是什么。所以我在 Google 上搜索了“什么是多租户架构”、“如何在 Node.js 中构建多租户架构”等等。不幸的是,我找不到很多有用的内容,但最后,我发现了 Knex(发音为 /kəˈnɛks/),它是一个强大的 SQL 查询构建器。我想借此机会分享一些我希望一些人会发现相关和有用的东西。
多租户架构是一种软件架构,其中软件的单个实例可以服务于多个用户组。这些用户组称为租户。
[](https://res.cloudinary.com/practicaldev/image/fetch/s--6RX9Vs1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev -to-uploads.s3.amazonaws.com/uploads/articles/e8jezfnv91d2gwb2x8i9.jpg)
在单租户中,
-
单独的应用程序。
-
独立数据库
在多租户中,
-
相同申请
-
独立数据库
多租户类型
在分离租户数据时,有两种主要的多租户架构模型
-
每个租户的单一数据库
-
单个数据库,每个租户都有不同的表
[](https://res.cloudinary.com/practicaldev/image/fetch/s--VCaNR2ou--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev -to-uploads.s3.amazonaws.com/uploads/articles/2f5cryemk9llfn6xtdfl.jpg)
Knex.js
根据其官方网站,Knex.js 是一个“包含电池”的 SQL 查询构建器,适用于 PostgreSQL、CockroachDB、MSSQL、MySQL、MariaDB、SQLite3、Better-SQLite3、Oracle 和 Amazon Redshift,旨在灵活、便携、使用起来很有趣。
现在让我们动手在 Node.js 中构建多租户架构。在以下示例中,我们将为每个租户的方法在单个数据库中使用不同的表。
先决条件
- 带有 express 库的 Node.js 基础知识
设置
创建一个新文件夹并通过在 CLI 中输入以下命令创建package.json
文件来初始化 node.js 项目。
$ npm init -y
进入全屏模式 退出全屏模式
安装express
、knex
和pg
软件包。 (pg 是 Node.js 的 PostgreSQL 客户端)
$ npm install express knex pg
进入全屏模式 退出全屏模式
在根文件夹中创建index.js
文件。这将是应用程序的入口点。
数据库架构
[](https://res.cloudinary.com/practicaldev/image/fetch/s--xK5ca3Y9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to- uploads.s3.amazonaws.com/uploads/articles/huhaoayq35hezbwvf4hk.png)
代码说明
注意:为了便于理解,我没有使用最佳代码实践
1\。 Knex 配置
创建一个db.js
文件。这是我们将配置 Knex 以建立与数据库的连接的地方。
db.js
使用require()
函数导入knex
模块。
const knex = require("knex");
进入全屏模式 退出全屏模式
调用由 Knex 模块导出的顶级函数knex()
,该函数接受一个配置对象,并接受一些参数。
const db = knex({
client: "postgresql",
connection: {
database: "mutitenancy-node",
user: "postgres",
password: "postgres",
},
});
进入全屏模式 退出全屏模式
导出db
变量,以便它可以在应用程序的其他地方使用。
module.exports = db;
进入全屏模式 退出全屏模式
2\。设置基本服务器
index.js
使用require()
函数在您的应用程序中导入 express。
const express = require("express");
进入全屏模式 退出全屏模式
调用 express 模块导出的顶层函数express()
。
const app = express()
进入全屏模式 退出全屏模式
在db.js
文件中导入默认导出功能
const knex = require("./db.js");
进入全屏模式 退出全屏模式
使用app
对象的use()
方法挂载express.json()
中间件函数,解析请求体中的 JSON。
app.use(express.json());
进入全屏模式 退出全屏模式
通过app对象的listen()
方法监听服务器。
app.listen(4000, () => {
console.log("Server listening to Port 4000");
});
进入全屏模式 退出全屏模式
3\。创建租户
通过迁移或手动使用以下字段创建tenants
表。
-
id - uuid
-
名称 - 字符不同
-
子域 - 字符不同
-
admin_email - 字符不同
每当新租户注册到我们的 SaaS 应用程序时,将他们的详细信息插入tenants
表,并为租户的用户创建一个users
表,前缀为子域名 (tenantname_users
)。
使用创建 POST 请求路由/create-tenant
app
对象的post()
方法。
app.post('/create-tenant', async (req, res) => {
})
进入全屏模式 退出全屏模式
在回调函数体中,从请求体中获取name
、subdomain
、adminEmail
属性的值。
const { name, subdomain, adminEmail } = req.body;
进入全屏模式 退出全屏模式
将租户的详细信息插入到tenants
表中
await knex("tenants").insert({
name,
subdomain,
admin_email: adminEmail,
});
进入全屏模式 退出全屏模式
现在,为租户的用户创建一个表
await knex.schema.createTable(`${subdomain}_users`, (table) => {
table.uuid("id").defaultTo(knex.raw("uuid_generate_v4()"));
table.string("first_name");
table.string("last_name");
table.string("email").unique();
});
进入全屏模式 退出全屏模式
使用send()
方法将响应发送回客户端。
res.send("Tenant Created");
进入全屏模式 退出全屏模式
4\。插入users
表
使用创建 POST 请求路由/create-user
app
对象的post()
方法。
app.post('/create-user', async (req, res) => {
})
进入全屏模式 退出全屏模式
使用req
对象的subdomains
数组获取请求客户端的子域。
const subdomain = req.subdomains[0];
进入全屏模式 退出全屏模式
注意:要为每个租户提供动态 URL,您可能需要在前端应用程序中使用 Nginx。
由于我们使用的是 localhost,因此请从请求正文中获取子域和用户详细信息。
const { firstName, lastName, email, subdomain } = req.body;
进入全屏模式 退出全屏模式
将用户的详细信息插入到该特定租户的users
表中
await knex(`${subdomain}_users`).insert({
first_name: firstName,
last_name: lastName,
email,
});
进入全屏模式 退出全屏模式
使用send()
方法将响应发送回客户端。
res.send("User Created !!");
进入全屏模式 退出全屏模式
总结
由于我们使用的是单个数据库,来自多个租户的请求可能会导致 noisy-neighbor effect,从而导致网络性能问题。
完整代码
Github
学分
-
由 Anggara 创建的 Seo 和 web 图标 - Flaticon
-
Freepik 创建的用户图标 - Flaticon
-
Smashicons 创建的数据库图标 - Flaticon
更多推荐
所有评论(0)