你的 TypeScript 类型,在运行时强制执行

一个 TypeScript 类型即可验证并序列化应用程序的每一个边界:HTTP、RPC、数据库、配置和 CLI。当代码在类型上出现细微错误时,无论是你还是智能体编写的,它都会在边界处以精确的错误失败,而不是让错误数据流过。

Discordnpm
npm init @deepkit/app@latest my-app

你是 LLM 吗?跳过这些像素吧,我们专门为你写了 markdown: llms.txt (llms-full.txt)

一个类型,贯穿整个技术栈

类型即是验证器、JSON 格式、数据库 schema 和 API 文档。无需同步维护 Zod schema、class-validator DTO 或 Prisma 模型。

每个边界都严格校验

每个 HTTP 请求、RPC 调用、数据库行、配置值和 CLI 参数都会在运行时根据其类型进行检查,并给出出错位置的精确路径。

JIT 编译

验证器、序列化器和数据库映射器会针对你的类型编译为普通的 JavaScript 函数。严格检查,却无需付出性能代价。

纯粹的 TypeScript

没有 schema DSL,没有用于依赖注入的装饰器,没有代码生成。就是你已经在写的 TypeScript,也是语言模型最擅长编写的 TypeScript。

一个类型覆盖整个后端

下面的 User 类同时是验证规则、JSON 格式、数据库表和 API 文档。HTTP 请求体会在你的代码运行之前被验证和转换,无效输入将返回 400 以及精确的错误信息。

import { App } from '@deepkit/app';
import { FrameworkModule } from '@deepkit/framework';
import { HttpBody, HttpQuery, HttpRouterRegistry } from '@deepkit/http';
import { Database } from '@deepkit/orm';
import { SQLiteDatabaseAdapter } from '@deepkit/sqlite';
import { AutoIncrement, Email, Maximum, MinLength, PrimaryKey, Unique } from '@deepkit/type';

// validation, serialization, database table and API docs in one type
class User {
    id: number & PrimaryKey & AutoIncrement = 0;
    created: Date = new Date();
    username: string & MinLength<3> & Unique = '';
    email: Email = '';
}

class AppDatabase extends Database {
    constructor() {
        super(new SQLiteDatabaseAdapter('app.sqlite'), [User]);
    }
}

const app = new App({
    providers: [AppDatabase],
    imports: [new FrameworkModule({ migrateOnStartup: true })],
});
const router = app.get(HttpRouterRegistry);

// POST /users {"username": "peter", "email": "[email protected]"}
router.post('/users', async (user: HttpBody<User>, database: AppDatabase) => {
    await database.persist(user);
    return user;
});

// GET /users?limit=10
router.get('/users', (database: AppDatabase, limit: HttpQuery<number> & Maximum<100> = 10) => {
    return database.query(User).limit(limit).find();
});

app.run();
$ curl -X POST localhost:8080/users -d '{"username": "pe", "email": "nope"}'
HTTP/1.1 400 Bad Request
{
  "message": "Validation error:\nusername(minLength): Min length is 3 caused by value \"pe\",\nemail(pattern): Pattern ^\\S+@\\S+$ does not match caused by value \"nope\"",
  "errors": [
    { "path": "username", "code": "minLength", "message": "Min length is 3", "value": "pe" },
    { "path": "email", "code": "pattern", "message": "Pattern ^\\S+@\\S+$ does not match", "value": "nope" }
  ]
}

入门

智能体编写的 TypeScript 的安全网

运行时类型使 Deepkit 在面对智能体编写的代码时具有独特优势。应用程序的每个边界都会在运行时根据 TypeScript 类型进行验证和序列化,因此生成的代码会在错误代价高昂的地方受到检查。

错误在发生之处明确暴露

错误的字段名,或在应为数字的位置出现字符串,都会在边界处被拒绝并给出精确路径,而不是被存入数据库、数周后才被发现。

没有新的出错点

在你的类型之外不存在第二种 schema 语言,模型也就不会让它们产生细微的不同步。只要 TypeScript 正确,验证、数据库和 API 就都正确。

一个技术栈,一套约定

HTTP、RPC、ORM、依赖注入、配置和 CLI 都来自同一个框架,而不是拼凑在一起的五个库,因此生成的代码在整个应用程序中保持一致。

因为了解你的类型,所以快速

Deepkit 在运行时将针对你的类型的验证器、序列化器和数据库映射器编译为普通的 JavaScript 函数。每次操作耗时,越低越好,与最常用的库进行对比测量。

带错误消息的验证

Deepkit
184 ns
typia 15.2.0
992 ns
Zod 4.6.7
1.4 µs
Valibot 1.5.0
1.9 µs
TypeBox 0.34.52
8.4 µs
ArkType 2.2.9
20 µs
class-validator 0.15.1
27 µs

MongoDB:按 ID 查找单个文档

Deepkit
299 µs
mongodb (driver) 7.7.0
428 µs
Mongoose 9.11.4
492 µs
MikroORM 7.2.5
638 µs

PostgreSQL:按 ID 查找单行

Deepkit
377 µs
Sequelize 6.37.8
424 µs
Drizzle 0.45.4
431 µs
TypeORM 1.1.2
446 µs
Prisma 7.10.1
533 µs
MikroORM 7.2.5
603 µs

PostgreSQL:插入 1,000 行

Deepkit
62 ms
Sequelize 6.37.8
64 ms
TypeORM 1.1.2
73 ms
MikroORM 7.2.5
75 ms
Drizzle 0.45.4
85 ms
Prisma 7.10.1
126 ms

所有基准测试

运行时类型:无需 schema 的验证与序列化

类型编译器使你的 TypeScript 代码中的类型在运行时可用,包括接口、泛型、联合类型以及 Omit 等映射类型。使用你已有的类型进行验证、类型守卫、类型转换和序列化,而无需在旁边另写 Zod schema 或 class-validator 装饰器。

了解更多
interface Order {
    id: number & Positive;
    items: { sku: string & MinLength<3>; quantity: number }[];
    shipped?: Date;
}

if (is<Order>(data)) {
    data.items; // type guard, data is an Order here
}

validate<Order>(data);
// [{ path: 'items.0.sku', code: 'minLength', ... }]

cast<Order>({ id: '1', items: [], shipped: '2026-10-31' });
// { id: 1, items: [], shipped: Date }

typeOf<Order>(); // all type information at runtime

带类型化参数的 HTTP 路由

路由参数、查询字符串和请求体会根据其 TypeScript 类型进行转换和验证,返回值则根据其类型进行序列化。支持函数式路由或控制器类,两者均支持依赖注入。

了解更多
// GET /users/34
router.get('/users/:id', (id: number & Positive, db: AppDatabase) =>
    db.query(User).filter({ id }).findOne(),
);

// or as controller class
class UserController {
    constructor(private db: AppDatabase) {}

    // GET /users?limit=10
    @http.GET('/users')
    list(limit: HttpQuery<number> & Maximum<100> = 10) {
        return this.db.query(User).limit(limit).find();
    }
}

无需装饰器的依赖注入

构造函数参数根据其类型解析,包括接口。无需 @Injectable(),无需注入令牌,无需 reflect-metadata。模块、作用域和配置都属于同一个容器,该容器会被编译为普通的 JavaScript。

了解更多
interface Mailer {
    send(to: string, text: string): Promise<void>;
}

class SmtpMailer implements Mailer {
    async send(to: string, text: string) { /* ... */ }
}

class Signup {
    // resolved by its type, an interface
    constructor(private mailer: Mailer) {}

    async register(email: string) {
        await this.mailer.send(email, 'Welcome!');
    }
}

const app = new App({
    providers: [Signup, provide<Mailer>(SmtpMailer)],
});

适用于 PostgreSQL、MySQL、SQLite 和 MongoDB 的 TypeScript ORM

实体是带有类型注解的类或接口。表、索引和迁移均由类型推导而来。提供工作单元、标识映射、关系以及返回真实类实例的类型安全查询 API。

了解更多
class User {
    id: number & PrimaryKey & AutoIncrement = 0;
    created: Date = new Date();
    constructor(
        public username: string & MinLength<3> & Unique,
        public active: boolean = true,
    ) {}
}

const database = new Database(
    new PostgresDatabaseAdapter({ database: 'app' }), [User],
);
await database.migrate();
await database.persist(new User('peter'));

const users = await database.query(User)
    .filter({ active: true, created: { $gt: lastWeek } })
    .orderBy('created', 'desc')
    .limit(10)
    .find(); // User[], real instances

服务器与客户端之间的类型安全 RPC

通过 WebSocket 或 TCP,像调用本地函数一样调用服务器控制器的方法。参数和结果会根据其类型进行验证和序列化,包括类、日期以及用于流的 RxJS Observable。

了解更多
// server
@rpc.controller('/users')
class UserController {
    constructor(private db: AppDatabase) {}

    @rpc.action()
    async find(username: string): Promise<User | undefined> {
        return await this.db.query(User)
            .filter({ username })
            .findOneOrUndefined();
    }
}

// client, in the browser or another server
const client = new RpcWebSocketClient('ws://localhost:8080');
const users = client.controller<UserController>('/users');

const user = await users.find('peter'); // User | undefined

命令行、配置与模块

命令从函数签名中获取其参数和标志。配置是一个类,从环境变量加载并在启动时验证;服务只会接收到它们所需的那部分配置。

了解更多
class AppConfig {
    databaseUrl: string = 'postgres://localhost/app';
    pageSize: number & Positive = 20;
}

class UserList {
    // only the part of the configuration it needs
    constructor(private pageSize: AppConfig['pageSize']) {}
}

const app = new App({
    config: AppConfig,
    providers: [UserList],
});

// $ app user:create peter --admin
app.command('user:create', (
    username: string & MinLength<3>,
    admin: boolean & Flag = false,
) => {
    // ...
});

// APP_PAGE_SIZE=50 sets pageSize, -1 fails at startup
app.loadConfigFromEnv().run();

库

Deepkit 的每个部分都是独立的包。你可以使用整个框架,也可以只使用你需要的库。

组合

从 App 开始构建 CLI 工具或服务,再添加 Framework 获得完整的服务器。每个库都以模块的形式接入,并拥有各自的配置和服务,因此项目可以从单个文件成长为模块化应用,而无需改变其基础。

基础设施

你的服务之间以及与客户端之间的通信。消息采用二进制编码,并在两端根据其类型进行验证。Broker 为应用的所有实例提供共享的队列、缓存和锁:开发时在内存中运行,生产环境中运行在 broker 服务器或 Redis 上,无需修改代码。

文件系统

一套 API 处理文件,无论它们存储在哪里。在本地磁盘上开发,在内存中测试,在生产环境中运行于 S3 或 Google Cloud Storage,只需在配置中切换适配器。读写文件的代码保持不变。

数据库

你的实体是 TypeScript 类或接口。相同的模型和相同的查询适用于 PostgreSQL、MySQL、SQLite 和 MongoDB,因此你可以在 SQLite 上测试,在 PostgreSQL 上部署,或日后更换数据库,而无需重写数据层。

基础

构成 Deepkit 的基础组件。每个组件都可以在任何 TypeScript 项目中独立使用,无论是后端、前端还是库;当你使用更多组件时,它们也能完美配合。

工具

基于你已编写的类型构建的工具:API 文档、数据库浏览器和调试器均来自你的代码,而不是需要你持续更新的独立定义。

核心

其他库所依赖的底层库,当你恰好需要某个部分时也可单独使用。

运行时

运行时类型需要在构建中使用类型编译器。它可与 TypeScript 编译器、Vite 和 Bun 配合使用,因此你可以继续使用现有的工具链。

开始使用

一条命令即可创建包含 HTTP、数据库和 CLI 的全新 Deepkit 应用。

npm init @deepkit/app@latest my-app

入门在 GitHub 上查看

Made in Germany