ToolJet架构深度解析:从数据库到前端的完整技术栈
ToolJet采用现代化的前后端分离架构,通过React + TypeScript构建前端应用,NestJS + TypeORM构建后端服务,PostgreSQL + Redis作为数据存储层。本文深度解析其完整的架构设计,包括模块化架构、RESTful API规范、插件系统与数据源集成机制、认证授权体系以及性能优化策略,展现一个企业级低代码平台的技术实现全貌。
前后端分离架构设计原理
ToolJet采用现代化的前后端分离架构,这种设计模式为低代码平台的开发提供了极大的灵活性和可扩展性。通过深入分析其架构设计,我们可以理解这种分离架构如何支撑起一个复杂的企业级应用构建平台。
架构分层与职责划分
ToolJet的前后端分离架构遵循清晰的责任边界原则,将系统划分为三个主要层次:
| 架构层级 | 技术栈 | 主要职责 |
|---|---|---|
| 前端层 | React + TypeScript | 用户界面渲染、组件交互、状态管理 |
| 后端层 | NestJS + TypeORM | 业务逻辑处理、数据持久化、API提供 |
| 数据层 | PostgreSQL + Redis | 数据存储、缓存管理、会话处理 |
RESTful API 设计规范
ToolJet的后端API严格遵循RESTful设计原则,为前端提供统一的接口规范:
// 典型的RESTful控制器结构示例
@Controller('apps')
export class AppsController {
constructor(private readonly appsService: AppsService) {}
@Get()
@UseGuards(JwtAuthGuard)
async findAll(@Query() query: AppQueryDto) {
return this.appsService.findAll(query);
}
@Get(':id')
@UseGuards(JwtAuthGuard)
async findOne(@Param('id') id: string) {
return this.appsService.findOne(id);
}
@Post()
@UseGuards(JwtAuthGuard)
async create(@Body() createAppDto: CreateAppDto) {
return this.appsService.create(createAppDto);
}
@Patch(':id')
@UseGuards(JwtAuthGuard)
async update(@Param('id') id: string, @Body() updateAppDto: UpdateAppDto) {
return this.appsService.update(id, updateAppDto);
}
@Delete(':id')
@UseGuards(JwtAuthGuard)
async remove(@Param('id') id: string) {
return this.appsService.remove(id);
}
}
模块化架构设计
ToolJet采用NestJS的模块化架构,将系统功能划分为多个独立的模块:
每个模块都包含完整的CRUD操作、业务逻辑和服务层,实现了高内聚低耦合的设计目标。
数据传输对象(DTO)模式
ToolJet大量使用DTO模式来确保前后端数据传输的类型安全和验证:
// 应用创建DTO示例
export class CreateAppDto {
@IsString()
@IsNotEmpty()
name: string;
@IsString()
@IsOptional()
description?: string;
@IsEnum(AppVisibility)
visibility: AppVisibility;
@IsUUID()
organizationId: string;
@ValidateNested()
@Type(() => CreatePageDto)
pages: CreatePageDto[];
}
// 对应的响应DTO
export class AppResponseDto {
id: string;
name: string;
description: string;
createdAt: Date;
updatedAt: Date;
organizationId: string;
currentVersionId: string;
}
认证与授权机制
前后端分离架构中的安全机制至关重要,ToolJet实现了完善的JWT认证和基于角色的访问控制:
// JWT认证守卫
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
canActivate(context: ExecutionContext) {
return super.canActivate(context);
}
handleRequest(err, user, info) {
if (err || !user) {
throw err || new UnauthorizedException();
}
return user;
}
}
// 能力-based访问控制
@Injectable()
export class AbilityGuard {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredPermissions = this.reflector.get<RequiredPermission[]>(
'permissions',
context.getHandler()
);
const { user } = context.switchToHttp().getRequest();
const ability = this.abilityFactory.createForUser(user);
return requiredPermissions.every(permission =>
ability.can(permission.action, permission.subject)
);
}
}
实时通信机制
为了支持多人协作编辑功能,ToolJet实现了基于WebSocket的实时通信:
错误处理与日志记录
前后端分离架构需要统一的错误处理机制:
// 全局异常过滤器
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
let status = HttpStatus.INTERNAL_SERVER_ERROR;
let message = 'Internal server error';
if (exception instanceof HttpException) {
status = exception.getStatus();
message = exception.message;
} else if (exception instanceof Error) {
message = exception.message;
}
// 记录错误日志
this.logger.error({
message,
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
stack: exception instanceof Error ? exception.stack : undefined,
});
response.status(status).json({
statusCode: status,
message,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
性能优化策略
前后端分离架构的性能优化是关键考虑因素:
- API响应缓存:使用Redis缓存频繁访问的数据
- 数据库查询优化:通过TypeORM的查询构建器优化复杂查询
- 前端资源优化:Webpack打包优化和代码分割
- CDN加速:静态资源通过CDN分发
- 连接池管理:数据库和Redis连接池优化
部署与运维考虑
分离架构带来的部署灵活性:
这种前后端分离架构使ToolJet能够实现快速迭代开发、独立部署和弹性扩展,为低代码平台提供了坚实的技术基础。通过清晰的接口定义和模块化设计,开发团队可以并行工作,提高开发效率,同时确保系统的稳定性和可维护性。
NestJS后端服务架构详解
ToolJet的后端服务基于NestJS框架构建,这是一个用于构建高效、可扩展Node.js服务器端应用程序的渐进式框架。NestJS提供了开箱即用的TypeScript支持、依赖注入、模块化架构等企业级特性,使其成为构建复杂低代码平台的理想选择。
模块化架构设计
ToolJet的NestJS后端采用高度模块化的设计,每个功能域都有独立的模块封装。核心模块结构如下:
核心依赖与技术栈
ToolJet后端集成了丰富的技术栈来支持其低代码平台的复杂需求:
| 技术组件 | 版本 | 用途 |
|---|---|---|
| @nestjs/common | ^11.1.3 | NestJS核心框架 |
| @nestjs/typeorm | ^11.0.0 | 数据库ORM集成 |
| typeorm | ^0.3.24 | 多数据库支持 |
| @nestjs/bull | ^11.0.2 | 队列处理 |
| @temporalio/client | ^1.11.6 | 工作流编排 |
| @nestjs/websockets | ^11.1.3 | 实时通信 |
| ioredis | ^5.0.4 | Redis缓存和会话管理 |
应用启动流程
ToolJet的启动流程经过精心设计,确保服务的稳定性和可靠性:
// 简化版启动流程
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(
await AppModule.register({ IS_GET_CONTEXT: false }),
{ bufferLogs: true, abortOnError: false }
);
// 配置中间件和管道
app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));
app.useGlobalFilters(new AllExceptionsFilter());
app.useGlobalInterceptors(new ResponseInterceptor());
// 设置安全头
setSecurityHeaders(app, configService);
// 启用API版本控制
app.enableVersioning({
type: VersioningType.URI,
defaultVersion: VERSION_NEUTRAL,
});
// 启动服务器
await app.listen(port, listen_addr);
}
数据库集成与迁移管理
ToolJet使用TypeORM作为主要的ORM工具,支持多种数据库系统。数据库迁移系统采用双数据源设计:
迁移脚本管理通过package.json中的命令实现:
{
"scripts": {
"db:migrate": "运行结构迁移",
"db:migrate:data": "运行数据迁移",
"db:seed": "填充种子数据",
"db:setup": "创建数据库并运行迁移"
}
}
安全性设计
ToolJet后端实施了多层次的安全防护措施:
- 输入验证:使用class-validator进行DTO验证
- 身份认证:JWT令牌和Passport策略
- 权限控制:基于CASL的能力系统
- 速率限制:@nestjs/throttler防止暴力攻击
- 安全头:Helmet中间件设置安全HTTP头
- SQL注入防护:TypeORM参数化查询
错误处理与日志系统
错误处理采用全局异常过滤器,日志系统基于Winston和Pino:
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
// 结构化错误响应
response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
message: exception.message,
});
}
}
实时通信与WebSocket集成
ToolJet使用@nestjs/websockets和ws库实现实时功能:
@WebSocketGateway({
cors: {
origin: process.env.NODE_ENV === 'production'
? process.env.APP_URL
: '*',
},
})
export class AppWebsocketGateway {
@WebSocketServer()
server: Server;
@SubscribeMessage('app-update')
handleAppUpdate(@MessageBody() data: any): Observable<WsResponse<any>> {
// 处理应用更新事件
return from([{ event: 'app-updated', data }]);
}
}
工作流引擎集成
ToolJet集成了Temporal工作流引擎来处理复杂的业务流程:
@Injectable()
export class TemporalService implements ITemporalService {
async runWorker() {
const worker = await Worker.create({
connection: this.temporalClient,
workflowsPath: require.resolve('@workflows'),
activities: this.getActivities(),
taskQueue: 'tooljet-queue',
});
await worker.run();
}
private getActivities() {
return {
executeQuery: this.executeQuery.bind(this),
processData: this.processData.bind(this),
sendNotification: this.sendNotification.bind(this),
};
}
}
性能优化策略
ToolJet后端采用了多种性能优化措施:
- 连接池管理:数据库和Redis连接池优化
- 缓存策略:Redis缓存频繁访问的数据
- 压缩中间件:gzip压缩响应数据
- 查询优化:TypeORM查询构建器优化
- 批量处理:使用Bull队列处理耗时任务
监控与可观测性
集成Sentry进行错误监控和性能追踪:
import * as Sentry from '@sentry/node';
import * as Tracing from '@sentry/tracing';
Sentry.init({
dsn: process.env.SENTRY_DSN,
integrations: [new Tracing.Integrations.Express({ app })],
tracesSampleRate: 1.0,
});
这种架构设计使得ToolJet后端能够处理大规模的企业级应用需求,同时保持代码的可维护性和扩展性。NestJS的模块化特性与TypeScript的强类型系统相结合,为开发团队提供了优秀的开发体验和代码质量保障。
React前端应用构建机制
ToolJet的前端架构采用了现代化的React技术栈,通过精心设计的构建流程和模块化架构,为低代码应用开发平台提供了强大的前端支撑。整个构建机制基于Webpack 5,集成了TypeScript支持、代码分割、热更新等先进特性,确保开发效率和运行时性能的最佳平衡。
Webpack配置架构
ToolJet的Webpack配置采用了多环境适配的设计模式,支持开发、生产等不同环境的构建需求。配置文件中定义了丰富的插件系统和优化策略:
// Webpack核心配置结构
module.exports = {
mode: environment, // 环境模式控制
optimization: {
minimize: environment === 'production',
runtimeChunk: 'single',
splitChunks: {
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
chunks: 'all'
}
}
}
},
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
alias: {
'@': path.resolve(__dirname, 'src/'),
'@ee': path.resolve(__dirname, 'ee/')
}
}
};
模块化架构设计
前端代码采用模块化架构,通过路径别名系统实现清晰的代码组织:
构建流程优化
构建流程通过多种优化技术提升性能:
| 优化技术 | 实现方式 | 效果 |
|---|---|---|
| 代码分割 | SplitChunksPlugin | 减少初始加载体积 |
| 压缩优化 | TerserPlugin | 减小文件大小 |
| Gzip压缩 | CompressionPlugin | 提升传输效率 |
| 树摇优化 | usedExports | 移除未使用代码 |
| 缓存策略 | 持久化缓存 | 加速重复构建 |
开发环境特性
开发环境配置了完整的开发工具链:
// 开发环境特定配置
if (isDevEnv) {
plugins.push(new ReactRefreshWebpackPlugin({ overlay: false }));
}
devServer: {
historyApiFallback: { index: ASSET_PATH },
static: {
directory: path.resolve(__dirname, 'assets'),
publicPath: '/assets/'
}
}
多版本支持机制
ToolJet支持CE(社区版)、EE(企业版)和Cloud版本,通过模块替换机制实现版本隔离:
new webpack.NormalModuleReplacementPlugin(/^(@ee\/|@cloud\/)/, (resource) => {
const edition = process.env.TOOLJET_EDITION || 'ce';
if (edition === 'ce' && resource.request.startsWith('@ee/')) {
resource.request = emptyModulePath;
}
});
国际化架构
前端采用i18next实现多语言支持,配置灵活的翻译文件加载机制:
i18n.use(Backend).use(initReactI18next).init({
load: 'languageOnly',
fallbackLng: 'en',
lng: language,
backend: {
loadPath: `${path}assets/translations/{{lng}}.json`
}
});
错误监控集成
集成Sentry进行前端错误监控和性能追踪:
Sentry.init({
dsn: window.public_config.SENTRY_DNS,
integrations: [
new Sentry.BrowserTracing({
routingInstrumentation: Sentry.reactRouterV6Instrumentation(
React.useEffect,
useLocation,
useNavigationType,
createRoutesFromChildren,
matchRoutes
)
})
],
tracesSampleRate: 0.5
});
构建输出配置
构建输出配置支持多种部署场景:
output: {
publicPath: ASSET_PATH,
path: path.resolve(__dirname, 'build')
},
externals: {
config: JSON.stringify({
apiUrl: `${stripTrailingSlash(API_URL[environment]) || ''}/api`,
ENVIRONMENT: process.env.NODE_ENV,
// ...其他配置项
})
}
这种构建机制确保了ToolJet前端应用的高性能、可维护性和扩展性,为复杂的低代码平台提供了坚实的技术基础。
插件系统与数据源集成架构
ToolJet的插件系统是其核心架构的重要组成部分,它提供了一个高度可扩展的框架来集成各种数据源和服务。该架构采用了模块化设计,支持50+种数据源的快速集成,包括数据库、API服务、云存储和AI服务等。
插件架构核心组件
ToolJet的插件系统由以下几个核心组件构成:
1. 插件清单文件 (Manifest.json)
每个插件都必须包含一个manifest.json文件,用于定义数据源的配置参数、UI组件和验证规则。以下是一个PostgreSQL插件的manifest示例:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Postgresql datasource",
"description": "A schema defining postgresql datasource",
"type": "object",
"tj:version": "1.0.0",
"tj:source": {
"name": "PostgreSQL",
"kind": "postgresql",
"type": "database"
},
"properties": {
"connection_type": {
"type": "string",
"title": "Connection type",
"description": "Single select dropdown for connection_type",
"enum": ["manual", "string"],
"default": "manual"
},
"host": {
"type": "string",
"title": "Host",
"description": "Enter host",
"default": "localhost"
},
"port": {
"type": "number",
"title": "Port",
"description": "Enter port",
"default": 5432
}
},
"tj:encrypted": ["password", "connection_string"],
"required": ["connection_type"]
}
2. 操作定义文件 (Operations.json)
operations.json文件定义了数据源支持的所有查询操作和对应的UI组件:
{
"$schema": "https://raw.githubusercontent.com/ToolJet/ToolJet/develop/plugins/schemas/operations.schema.json",
"title": "Postgresql datasource",
"description": "A schema defining Postgresql datasource",
"type": "database",
"defaults": {
"mode": "sql"
},
"properties": {
"mode": {
"label": "",
"key": "mode",
"type": "dropdown-component-flip",
"description": "Single select dropdown for mode",
"list": [
{
"name": "SQL mode",
"value": "sql"
},
{
"name": "GUI mode",
"value": "gui"
}
]
},
"sql": {
"query": {
"key": "query",
"type": "codehinter",
"description": "Enter query",
"height": "150px",
"editorType": "multiline",
"placeholder": "SELECT * FROM users"
}
}
}
}
3. 查询服务实现 (Index.ts)
每个插件都需要实现一个QueryService类,提供统一的接口来处理查询操作:
export default class PostgresqlQueryService implements QueryService {
async run(
sourceOptions: SourceOptions,
queryOptions: QueryOptions,
dataSourceId: string,
dataSourceUpdatedAt: string
): Promise<QueryResult> {
// 建立连接并执行查询
const knexInstance = await this.getConnection(sourceOptions);
switch (queryOptions.mode) {
case 'sql':
return await this.handleRawQuery(knexInstance, queryOptions);
case 'gui':
return await this.handleGuiQuery(knexInstance, queryOptions);
default:
throw new Error("Invalid query mode");
}
}
async testConnection(sourceOptions: SourceOptions): Promise<ConnectionTestResult> {
const knexInstance = await this.getConnection(sourceOptions);
await knexInstance.raw('SELECT version();');
return { status: 'ok' };
}
}
插件生命周期管理
ToolJet提供了完整的插件生命周期管理机制:
数据源集成流程
数据源的集成遵循标准化的流程:
1. 连接测试流程
async testConnection(testDataSourceDto: TestDataSourceDto): Promise<object> {
const { kind, options, plugin_id } = testDataSourceDto;
// 解析配置选项
const sourceOptions = await this.parseOptions(options);
// 获取插件服务实例
const service = await this.pluginsServiceSelector.getService(plugin_id, kind);
// 执行连接测试
if (!service?.testConnection) {
throw new NotImplementedException('testConnection method not implemented');
}
return await service.testConnection(sourceOptions);
}
2. 查询执行流程
插件选择器机制
PluginServiceSelector是插件系统的核心组件,负责动态加载和执行插件:
export class PluginsServiceSelector {
async getService(pluginId: string, kind: string) {
const isToolJetDatabaseKind = kind === 'tooljetdb';
const isMarketplacePlugin = !!pluginId;
try {
if (isToolJetDatabaseKind) return this.tooljetDbDataOperationsService;
if (isMarketplacePlugin) return await this.findMarketplacePluginService(pluginId);
return new allPlugins[kind]();
} catch (error) {
throw new InternalServerErrorException(error);
}
}
protected async findMarketplacePluginService(pluginId: string) {
const plugin = await this.pluginsRepository.findById(pluginId, ['indexFile']);
const decoded = decode(plugin.indexFile.data.toString());
const code = requireFromString(decoded, { useCurrentGlobal: true });
const service = new code.default();
return service;
}
}
安全与加密机制
ToolJet提供了完善的安全机制来保护敏感数据:
1. 凭据加密存储
async parseOptionsForCreate(options: Array<object>, resetSecureData = false) {
for (const option of options) {
if (option['encrypted']) {
const credential = await this.credentialService.create(
resetSecureData ? '' : option['value'] || ''
);
parsedOptions[option['key']] = {
credential_id: credential.id,
encrypted: option['encrypted'],
};
} else {
parsedOptions[option['key']] = {
value: option['value'],
encrypted: false,
};
}
}
return parsedOptions;
}
2. 动态连接参数支持
if (sourceOptions['allow_dynamic_connection_parameters']) {
if (sourceOptions.connection_type === 'manual') {
sourceOptions['host'] = queryOptions['host'] || sourceOptions['host'];
sourceOptions['database'] = queryOptions['database'] || sourceOptions['database'];
}
}
多环境支持
ToolJet支持多环境配置,确保开发、测试和生产环境的数据隔离:
| 环境类型 | 配置管理 | 数据隔离 | 用途 |
|---|---|---|---|
| 开发环境 | 独立配置 | 完全隔离 | 功能开发和测试 |
| 测试环境 | 独立配置 | 完全隔离 | 集成测试和验收 |
| 生产环境 | 独立配置 | 完全隔离 | 线上业务运行 |
插件类型分类
ToolJet支持多种类型的插件,每种类型都有特定的实现模式:
| 插件类型 | 示例 | 特点 | 使用场景 |
|---|---|---|---|
| 数据库插件 | PostgreSQL, MySQL | 支持SQL和GUI操作 | 传统数据库集成 |
| API服务插件 | REST API, GraphQL | HTTP请求处理 | 外部服务集成 |
| 云存储插件 | S3, Google Cloud Storage | 文件操作接口 | 文件存储管理 |
| AI服务插件 | OpenAI, Anthropic | 模型调用接口 | 人工智能应用 |
| 消息服务插件 | Slack, Twilio | 消息推送接口 | 通知和通信 |
性能优化策略
ToolJet在插件系统中实现了多种性能优化策略:
1. 连接池管理
private async getConnection(
sourceOptions: SourceOptions,
checkCache: boolean,
dataSourceId?: string,
dataSourceUpdatedAt?: string
): Promise<Knex> {
if (checkCache) {
const optionsHash = generateSourceOptionsHash(sourceOptions);
const enhancedCacheKey = `${dataSourceId}_${optionsHash}`;
const cachedConnection = await getCachedConnection(enhancedCacheKey, dataSourceUpdatedAt);
if (cachedConnection) return cachedConnection;
const connection = await this.buildConnection(sourceOptions);
cacheConnectionWithConfiguration(dataSourceId, enhancedCacheKey, connection);
return connection;
}
return await this.buildConnection(sourceOptions);
}
2. 查询超时控制
constructor() {
this.STATEMENT_TIMEOUT =
process.env?.PLUGINS_SQL_DB_STATEMENT_TIMEOUT &&
!isNaN(Number(process.env?.PLUGINS_SQL_DB_STATEMENT_TIMEOUT))
? Number(process.env.PLUGINS_SQL_DB_STATEMENT_TIMEOUT)
: 120000; // 默认120秒
}
错误处理与监控
插件系统提供了完善的错误处理机制:
try {
result = await service.run(
sourceOptions,
parsedQueryOptions,
`${dataSource.id}-${dataSourceOptions.environmentId}`,
dataSourceOptions.updatedAt,
{
user: { id: user?.id },
app: { id: app?.id, isPublic: app?.isPublic }
}
);
} catch (error) {
if (error.constructor.name === 'QueryError') {
result = {
status: 'failed',
message: error.message,
description: error.description,
data: error.data,
metadata: error.metadata,
};
} else {
result = {
status: 'failed',
message: 'Internal server error',
description: error.message,
data: {},
};
}
}
扩展性与自定义
ToolJet的插件架构支持高度自定义和扩展:
1. 自定义插件开发
开发者可以通过CLI工具快速创建新的插件:
npx @tooljet/cli create-plugin my-custom-datasource
2. 插件市场集成
ToolJet提供了完整的插件市场机制,支持插件的发现、安装和更新:
async autoInstallPluginsForTemplates(pluginsToBeInstalled: Array<string>, shouldAutoInstall: boolean) {
const { pluginsListIdToDetailsMap } = this.listMarketplacePlugins();
if (shouldAutoInstall && pluginsToBeInstalled.length) {
for (const pluginId of pluginsToBeInstalled) {
const pluginDetails = pluginsListIdToDetailsMap[pluginId];
await this.install(pluginDetails);
}
}
}
ToolJet的插件系统与数据源集成架构展现了一个现代化低代码平台的核心技术实现,通过标准化的接口设计、完善的安全机制和优秀的性能优化,为开发者提供了强大而灵活的数据集成能力。
架构总结
ToolJet的架构设计体现了现代化企业级应用的最佳实践:前后端分离架构提供了开发灵活性和部署独立性;模块化设计确保了系统的高内聚低耦合;插件系统支持50+数据源的快速集成;完善的安全机制保障了企业级应用的安全性;性能优化策略确保了系统的高效运行。这种架构不仅支撑了ToolJet作为低代码平台的核心功能,更为其未来的扩展和发展奠定了坚实的技术基础。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



