런타임에 강제되는 TypeScript 타입

하나의 TypeScript 타입이 애플리케이션의 모든 경계, 즉 HTTP, RPC, 데이터베이스, 설정 및 CLI를 검증하고 직렬화합니다. 코드를 직접 작성했든 에이전트가 작성했든, 코드가 타입을 미묘하게 잘못 다루면 잘못된 데이터를 그대로 통과시키는 대신 경계에서 정확한 오류와 함께 실패합니다.

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

LLM이신가요? 픽셀은 건너뛰세요. 여러분을 위해 마크다운을 준비했습니다: llms.txt (llms-full.txt)

하나의 타입, 전체 스택

타입이 곧 검증기이자 JSON 형식, 데이터베이스 스키마, API 문서입니다. 동기화를 유지해야 할 Zod 스키마, class-validator DTO 또는 Prisma 모델이 없습니다.

모든 경계에서 엄격하게

모든 HTTP 요청, RPC 호출, 데이터베이스 행, 설정 값 및 CLI 인자는 런타임에 해당 타입에 대해 검사되며, 무엇이 잘못되었는지 정확한 경로를 알려줍니다.

JIT 컴파일

검증기, 직렬화기 및 데이터베이스 매퍼는 여러분의 타입에 맞게 일반 JavaScript 함수로 컴파일됩니다. 성능 비용 없이 엄격한 검사를 제공합니다.

그저 TypeScript

스키마 DSL도, 의존성 주입용 데코레이터도, 코드 생성도 없습니다. 여러분이 이미 작성하고 있고, 언어 모델이 가장 잘 작성하는 바로 그 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 타입에 따라 검증하고 직렬화하므로, 생성된 코드는 실수의 대가가 큰 곳에서 검사됩니다.

실수는 발생한 곳에서 명확하게 실패합니다

잘못된 필드 이름이나 숫자가 들어가야 할 곳의 문자열은 데이터베이스에 저장되어 몇 주 후에 발견되는 것이 아니라, 정확한 경로와 함께 경계에서 거부됩니다.

새로 틀릴 것이 없습니다

모델이 미묘하게 동기화를 놓칠 수 있는, 타입과 별도의 두 번째 스키마 언어가 없습니다. TypeScript가 올바르면 유효성 검사, 데이터베이스, API도 올바릅니다.

하나의 스택, 하나의 규칙

HTTP, RPC, ORM, DI, 설정 및 CLI가 다섯 개의 라이브러리를 이어 붙인 것이 아니라 하나의 프레임워크에서 제공되므로, 생성된 코드가 전체 애플리케이션에서 일관성을 유지합니다.

타입을 알기에 빠릅니다

Deepkit은 런타임에 여러분의 타입에 맞는 검증기, 직렬화기 및 데이터베이스 매퍼를 일반 JavaScript 함수로 컴파일합니다. 작업당 소요 시간이며 낮을수록 좋고, 가장 많이 사용되는 라이브러리와 비교하여 측정했습니다.

오류 메시지를 포함한 유효성 검사

Deepkit
181 ns
typia 15.2.0
1.0 µs
Zod 4.6.7
1.4 µs
Valibot 1.5.0
2.6 µs
TypeBox 0.34.52
8.4 µs
ArkType 2.2.9
21 µs
class-validator 0.15.1
30 µs

MongoDB: id로 문서 하나 찾기

Deepkit
198 µs
mongodb (driver) 7.7.0
425 µs
Mongoose 9.11.4
509 µs
MikroORM 7.2.5
593 µs

PostgreSQL: id로 행 하나 찾기

Deepkit
160 µs
Sequelize 6.37.8
414 µs
Drizzle 0.45.4
420 µs
TypeORM 1.1.2
445 µs
Prisma 7.10.1
524 µs
MikroORM 7.2.5
598 µs

PostgreSQL: 1,000개 행 삽입

Deepkit
36 ms
TypeORM 1.1.2
40 ms
Sequelize 6.37.8
41 ms
MikroORM 7.2.5
52 ms
Drizzle 0.45.4
61 ms
Prisma 7.10.1
92 ms

전체 벤치마크

런타임 타입: 스키마 없는 유효성 검사와 직렬화

타입 컴파일러는 인터페이스, 제네릭, 유니언 및 Omit과 같은 매핑된 타입을 포함하여 TypeScript 코드의 타입을 런타임에 사용할 수 있게 해줍니다. 별도의 Zod 스키마나 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

엔티티는 타입 어노테이션이 있는 클래스 또는 인터페이스입니다. 테이블, 인덱스 및 마이그레이션은 타입에서 도출됩니다. 작업 단위(Unit of Work), 식별자 맵(Identity Map), 관계, 그리고 실제 클래스 인스턴스를 반환하는 타입 안전한 쿼리 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의 모든 부분은 독립된 패키지입니다. 전체 프레임워크를 사용하거나 필요한 라이브러리만 사용하세요.

컴포지션

CLI 도구나 서비스는 App으로 시작하고, 완전한 서버가 필요하면 Framework를 추가하세요. 모든 라이브러리는 자체 설정과 서비스를 갖춘 모듈로 연결되므로, 프로젝트는 기반을 바꾸지 않고도 단일 파일에서 모듈식 애플리케이션으로 성장할 수 있습니다.

인프라

서비스 간 및 클라이언트와의 통신. 메시지는 바이너리로 인코딩되며 양쪽 끝에서 타입으로 검증됩니다. 브로커는 앱의 모든 인스턴스에 공유 큐, 캐시 및 락을 제공합니다: 개발 중에는 메모리에서, 프로덕션에서는 브로커 서버나 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