npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

onela

v4.0.0

Published

Adaptive multi-database ORM framework for Node.js/TypeScript. One codebase, 9+ databases (MySQL, PostgreSQL, SQLite, SQL Server, Oracle, MariaDB, TiDB, OceanBase, PolarDB). Hot-switch, read-write split, failover, schema management, and security built-in.

Readme

Onela ORM

世界级自适应关系型数据库热切换 ORM 框架,一套业务代码无缝接入 9+ 种数据库。

npm TypeScript tree-shakable Downloads License

特性

  • 9+ 数据库支持 - MySQL / MariaDB / TiDB / PostgreSQL / SQLite / SQL Server / Oracle / OceanBase / PolarDB
  • 热切换 - 运行时无缝切换数据库连接,零停机
  • 读写分离 - 自动路由读写请求,支持 4 种负载均衡策略
  • 故障转移 - 自动检测故障节点并切换,支持健康检查
  • 5 种查询模式 - 简单对象 / 操作符函数 / 链式构建 / Lambda 表达式 / 传统模式
  • Schema 管理 - DDL 构建、结构自省、迁移运行、动态 Model 注册
  • 安全体系 - SQL 注入防护 / 操作白名单 / 审计日志 / 行级限制 / 字段级访问控制
  • AI 友好 - 动态 Model、结构描述、元数据查询,为 AI 数据库管理场景设计
  • TypeScript 原生 - 完整类型定义,100% Tree-shakable

安装

npm install onela

安装数据库驱动(按需选择):

npm install mysql2       # MySQL / MariaDB / TiDB / OceanBase / PolarDB
npm install pg           # PostgreSQL
npm install better-sqlite3  # SQLite(推荐)
npm install sqlite3         # SQLite(异步)
npm install tedious         # SQL Server
npm install oracledb     # Oracle

快速开始

1. 初始化

import { Onela, OnelaBaseModel } from 'onela';
import type { Configs } from 'onela';

// 推荐:使用 V2 适配器(基于统一 SQLBuilder 架构)
Onela.init([
  {
    engine: 'default',
    type: 'mysql',
    value: {
      host: 'localhost',
      port: 3306,
      user: 'root',
      password: 'password',
      database: 'mydb',
      connectionLimit: 10,
    },
  },
], { useV2: true });

2. 定义 Model

class User extends OnelaBaseModel {
  static configs: Configs = {
    engine: 'default',
    tableName: 'users',
    fields: [
      { name: 'id', type: 'int', default: null, comment: '主键', primary: true, increment: true },
      { name: 'name', type: 'varchar', default: '', comment: '用户名' },
      { name: 'email', type: 'varchar', default: '', comment: '邮箱' },
      { name: 'age', type: 'int', default: 0, comment: '年龄' },
      { name: 'status', type: 'tinyint', default: 1, comment: '状态' },
      { name: 'created_at', type: 'datetime', default: () => new Date(), comment: '创建时间' },
    ],
  };
}

3. CRUD 操作

// 查询单条
const user = await User.findOne({
  where: [{ key: 'id', value: 1, operator: '=' }],
});

// 查询列表
const users = await User.findAll({
  where: [{ key: 'status', value: 1, operator: '=' }],
  orderBy: { created_at: 'DESC' },
  limit: [0, 20],
});

// 分页查询
const { data, recordsTotal } = await User.findList({
  where: [{ key: 'status', value: 1, operator: '=' }],
  limit: [0, 10],
});

// 插入
const result = await User.insert({ name: 'Alice', email: '[email protected]', age: 25 });

// 更新
await User.update({
  update: [{ key: 'name', value: 'Bob', operator: 'replace' }],
  where: [{ key: 'id', value: 1, operator: '=' }],
});

// 删除
await User.delete({
  where: [{ key: 'id', value: 1, operator: '=' }],
});

5 种查询模式

模式 1:简单对象

import { parseSimpleWhere } from 'onela';

const keyword = parseSimpleWhere({ name: 'Alice', status: 1 });
User.findAll({ where: keyword });

模式 2:操作符函数

import { Op } from 'onela';

User.findAll({
  where: [Op.eq('status', 1), Op.gte('age', 18), Op.like('name', 'Ali')],
});

模式 3:链式构建

import { createQueryBuilder } from 'onela';

const qb = createQueryBuilder(User.configs);
qb.select('id', 'name', 'email')
  .where('status', 1)
  .where('age', '>=', 18)
  .orderBy('created_at', 'DESC')
  .page(1, 20);

User.findAll(qb.build());

模式 4:Lambda 表达式

const qb = createQueryBuilder<{ name: string; age: number; status: number }>(User.configs);
qb.whereLambda((f) => [f.status.eq(1), f.age.gte(18)])
  .orderByDesc('created_at')
  .limit(20);

模式 5:传统模式

User.findAll({
  where: [
    { key: 'status', value: 1, operator: '=', logic: 'and' },
    { key: 'age', value: 18, operator: '>=', logic: 'and' },
  ],
  orderBy: { created_at: 'DESC' },
  limit: [0, 20],
});

高级功能

事务

const t = await User.transaction();
try {
  await User.insert({ name: 'Alice', email: '[email protected]' }, { transaction: t });
  await User.update({
    update: [{ key: 'status', value: 2, operator: 'replace' }],
    where: [{ key: 'name', value: 'Alice', operator: '=' }],
  }, { transaction: t });
  await t.commit();
} catch (e) {
  await t.rollback();
}

JOIN 查询

import { createJoinBuilder } from 'onela';

const jb = createJoinBuilder('users', 'u');
jb.leftJoin('orders', [{ leftColumn: 'u.id', rightColumn: 'orders.user_id' }], 'o')
  .select('u', 'name', 'user_name')
  .select('o', 'amount');

const sql = jb.buildSelectSQL('u.status = 1', 'o.created_at DESC', '20');

子查询

import { createSubqueryBuilder, subqueryIn } from 'onela';

// WHERE user_id IN (SELECT id FROM users WHERE status = 1)
const sub = subqueryIn('user_id', 'users', 'id', 'status = ?', 1);

// EXISTS 子查询
const builder = createSubqueryBuilder('orders')
  .select('1')
  .where('orders.user_id = users.id');
const exists = builder.toExistsClause();

聚合查询

User.aggregate({
  aggregate: [
    { function: 'count', field: 'id', name: 'total' },
    { function: 'avg', field: 'age', name: 'avgAge' },
  ],
  where: [{ key: 'status', value: 1, operator: '=' }],
});

复杂更新 (CASE WHEN)

User.update({
  update: [
    { key: 'status', value: 1, operator: 'replace' },
    {
      key: 'name',
      value: null,
      case_field: 'id',
      case_item: [
        { case_value: 1, value: 'Alice Updated', operator: 'replace' },
        { case_value: 2, value: 'Bob Updated', operator: 'replace' },
      ],
    },
  ],
  where: [{ key: 'id', operator: 'in', value: [1, 2] }],
});

原生 SQL

const result = await User.sql('SELECT * FROM users WHERE age > ?', [18]);

Schema 管理

DDL 构建

import { createDDLBuilder } from 'onela';

const ddl = createDDLBuilder('mysql');

// CREATE TABLE
const sql = ddl.buildCreateTable({
  tableName: 'articles',
  ifNotExists: true,
  columns: [
    { name: 'id', type: 'int', primary: true, increment: true },
    { name: 'title', type: 'varchar', length: 200, nullable: false },
    { name: 'content', type: 'text' },
    { name: 'created_at', type: 'datetime', defaultValue: 'CURRENT_TIMESTAMP' },
  ],
  engine: 'InnoDB',
  charset: 'utf8mb4',
  comment: '文章表',
});

// ALTER TABLE
const alterSQLs = ddl.buildAlterTable('articles', [
  { type: 'addColumn', column: { name: 'views', type: 'int', defaultValue: 0 } },
  { type: 'addIndex', index: { name: 'idx_title', columns: ['title'] } },
]);

数据库自省

import { createSchemaIntrospector } from 'onela';

const introspector = createSchemaIntrospector('mysql', (sql, params) => User.sql(sql, params));

// 获取所有表
const tables = await introspector.getTables();

// 获取表结构
const schema = await introspector.getTableSchema('users');

// 转换为 Onela Model 配置
const configs = SchemaIntrospector.toConfigs('users', 'default', schema.columns);

迁移管理

import { createMigrationRunner } from 'onela';

const runner = createMigrationRunner('mysql', (sql, params) => User.sql(sql, params));

const migrations = [
  {
    version: '20260101_001',
    description: 'Create articles table',
    up: 'CREATE TABLE articles (id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(200))',
    down: 'DROP TABLE articles',
  },
];

// 执行迁移
await runner.up(migrations);

// 查看状态
const status = await runner.status(migrations);

// 回滚
await runner.down(migrations, 1);

动态 Model(AI 场景)

import { DynamicModelRegistry } from 'onela';

// 运行时注册 Model
DynamicModelRegistry.createFromFields('ai_data', 'default', [
  { name: 'id', type: 'int', default: null, comment: 'ID', primary: true, increment: true },
  { name: 'content', type: 'text', default: '', comment: '内容' },
]);

// 获取所有 Model 描述(供 AI 使用)
const description = DynamicModelRegistry.describe();

安全体系

SQL 注入防护

import { createSQLInjectionPrevention } from 'onela';

const security = createSQLInjectionPrevention({
  enabled: true,
  throwOnDetection: true,
  maxValueLength: 10000,
});

security.check(userInput); // 检测到注入时抛出异常

操作白名单

import { createOperationGuard } from 'onela';

const guard = createOperationGuard({
  defaultPolicy: 'deny',
  permissions: [
    { tableName: 'users', allowed: ['select', 'update'] },
    { tableName: 'logs', readonly: true },
  ],
  globalDeny: ['sql'],
});

guard.assert('users', 'delete'); // Error: Operation "delete" is not allowed

审计日志

import { createAuditLogger } from 'onela';

const audit = createAuditLogger({
  enabled: true,
  logSQL: true,
  operations: ['insert', 'update', 'delete'],
});

await audit.log({
  operation: 'update',
  tableName: 'users',
  engine: 'default',
  success: true,
  affectedRows: 1,
  duration: 15,
});

const logs = await audit.query({ tableName: 'users', failedOnly: true });

行级限制

import { createRowLimiter } from 'onela';

const limiter = createRowLimiter({
  defaultLimits: { select: 10000, delete: 100, update: 500 },
});

limiter.assert('users', 'delete', 5000); // Error: exceeding limit of 100

字段级访问控制

import { createFieldAccessControl } from 'onela';

const acl = createFieldAccessControl({
  tables: [{
    tableName: 'users',
    defaultPermission: 'readwrite',
    rules: [
      { field: 'password', permission: 'none' },
      { field: 'email', permission: 'read' },
    ],
  }],
});

acl.canRead('users', 'password');   // false
acl.canWrite('users', 'email');     // false

const safeFields = acl.filterReadableFields('users', ['id', 'name', 'password']);
// ['id', 'name']

连接路由

读写分离 & 负载均衡

import { createConnectionRouter } from 'onela';

const router = createConnectionRouter({
  readWriteSplit: true,
  strategy: 'least-connections', // round-robin | random | weight | least-connections
  failover: true,
  maxRetries: 3,
  healthCheckInterval: 30000,
});

router.addNode({ id: 'master', role: 'master', weight: 1, enabled: true, healthy: true, config: masterConfig, type: 'mysql' });
router.addNode({ id: 'slave-1', role: 'slave', weight: 2, enabled: true, healthy: true, config: slave1Config, type: 'mysql' });

// 自动路由
const writeNode = router.getWriteNode();
const readNode = router.getReadNode();

// 带重试的执行
await router.executeWithRetry(async (node) => {
  // 数据库操作
}, true); // true = 写操作

// 热切换
await router.hotSwitch('old-master', newMasterNode);

// 查看状态
const status = router.getStatus();

错误处理

import { OnelaError, ErrorCode, isOnelaError, wrapError } from 'onela';

try {
  await User.findAll({ where: [{ key: 'id', value: 1, operator: '=' }] });
} catch (e) {
  if (isOnelaError(e)) {
    console.log(e.code);       // ErrorCode.QUERY_FAILED (2001)
    console.log(e.category);   // 'query'
    console.log(e.isRetryable()); // false
    console.log(e.toJSON());   // 结构化错误信息
  }
}

// 错误码分类:
// 1xxx - 连接错误 (CONNECTION_FAILED, CONNECTION_TIMEOUT, ...)
// 2xxx - 查询错误 (QUERY_FAILED, INVALID_SQL, ...)
// 3xxx - 配置错误 (INVALID_CONFIG, MISSING_ENGINE, ...)
// 4xxx - 安全错误 (SQL_INJECTION_DETECTED, OPERATION_NOT_ALLOWED, ...)
// 5xxx - 事务错误 (TRANSACTION_FAILED, TRANSACTION_TIMEOUT, ...)
// 6xxx - Schema 错误 (DDL_FAILED, MIGRATION_FAILED, ...)

元数据查询

import { createMetadataQuery } from 'onela';

const meta = createMetadataQuery('mysql');

// 跨数据库通用的元数据 SQL
const versionSQL = meta.getVersionSQL();
const tablesSQL = meta.getDatabasesSQL();
const indexesSQL = meta.getIndexesSQL('users');
const foreignKeysSQL = meta.getForeignKeysSQL('orders');
const sizeSQL = meta.getTableSizeSQL('users');

数据库支持矩阵

| 数据库 | 类型标识 | 驱动 | V2 适配器 | |--------|---------|------|----------| | MySQL 5.7+ / 8.0+ | mysql | mysql2 | MySQLActionManagerV2 | | MariaDB 10.x+ | mariadb | mysql2 | MySQLActionManagerV2 | | TiDB | tidb | mysql2 | MySQLActionManagerV2 | | OceanBase | oceanbase | mysql2 | MySQLActionManagerV2 | | PolarDB | polardb | mysql2 | MySQLActionManagerV2 | | PostgreSQL 10+ | postgresql | pg | PostgreSQLActionManagerV2 | | SQLite 3.x | sqlite | sqlite3 | SQLiteActionManagerV2 | | SQL Server 2012+ | sqlserver | tedious | SQLServerActionManagerV2 | | Oracle 11g+ | oracle | oracledb | OracleActionManagerV2 |

条件操作符

| 操作符 | 说明 | 示例值 | |-------|------|-------| | = | 等于 | 1 | | > < >= <= <> | 比较 | 18 | | in / not in | 集合 | [1, 2, 3] | | between / not between | 范围 | [1, 100] | | %% | 全模糊 LIKE '%v%' | 'test' | | % | 左模糊 LIKE '%v' | 'test' | | x% | 右模糊 LIKE 'v%' | 'test' | | like / not like | LIKE | '%test%' | | is / is not | NULL 判断 | 'NULL' | | regexp / ~ | 正则 | '^A' |

V1 → V2 迁移

v4.0 起,V1 适配器已标记为 @deprecated,将在 v5.0 移除。详见 Migration Guide

关键变更:

  • 初始化时传入 { useV2: true } 启用 V2 适配器
  • V2 基于统一的 SQLBuilder + AbstractActionManager 架构,SQL 构建集中化
  • V1 的 format 属性已废弃(安全风险),建议使用参数化查询
  • $raw 操作符已禁用

项目结构

src/
  index.v2.ts            # 入口文件
  types/onela.ts         # 类型定义
  abstract/              # 抽象基类 (AbstractActionManager)
  interfaces/            # 接口定义 (IActionManager)
  dialect/               # 方言系统 (MySQL, PostgreSQL, SQLite, SQLServer, Oracle)
  builders/              # SQL 构建器
  query/                 # 查询模块 (QueryBuilder, JoinBuilder, SubqueryBuilder, Op, MetadataQuery)
  instance/              # 数据库适配器 (V1 + V2)
  router/                # 连接路由器 (读写分离, 负载均衡, 热切换)
  security/              # 安全模块 (注入防护, 操作守卫, 审计, 行限制, 字段控制)
  schema/                # Schema 管理 (DDL, 自省, 迁移, 动态Model)
  errors/                # 统一错误系统 (OnelaError, ErrorCode)
  logger/                # 日志模块

构建 & 测试

npm run build          # 构建 (Rollup, 输出 ESM + CJS)
npm test               # 运行全部测试
npm run test:unit      # 运行单元测试
npm run test:integration  # 运行集成测试

License

GPL-3.0-only

Author

SHIYE - @zouwei