Skip to content

Build Blueprint Interfaces

The interfaces build command turns your local *.blueprint.json files into typed interfaces for TypeScript or Python. Once generated, the SDK's entitiesOf(...) calls become fully typed — autocomplete, hovers, and compile-time checks for every blueprint property and relation, with no manual generic parameters.

Pair this with qelos pull blueprints to keep your local blueprint definitions in sync with your Qelos instance.

Usage

bash
qelos interfaces build [path] [options]

Arguments

ArgumentDescriptionDefault
pathDirectory containing *.blueprint.json files../blueprints

Options

OptionDescriptionDefault
--langOutput language. Either ts or py.ts
--outOutput directory for the generated module../types
--savePersist current options into qelos.config.json under interfaces.

What the command does

  1. Reads every *.blueprint.json file in the source directory.
  2. Skips files missing an identifier (with a warning).
  3. Generates a single declaration file:
    • TypeScript<out>/qelos-blueprints.d.ts
    • Python<out>/qelos_blueprints.py
  4. TypeScript only: locates tsconfig.json in the current directory and adds the generated file to the include array (idempotent — re-running won't duplicate the entry).

The TypeScript output also augments @qelos/sdk via declaration merging, so sdk.blueprints.entitiesOf('todo') resolves directly to the matching entity type.

Quick start

bash
# 1. Pull blueprints from your Qelos instance
qelos pull blueprints ./blueprints

# 2. Generate TypeScript interfaces (default)
qelos interfaces build

# => Loaded N blueprint(s) from .../blueprints
# => Generated TypeScript declarations: .../types/qelos-blueprints.d.ts
# => Updated tsconfig.json include with: ./types/qelos-blueprints.d.ts

That's it. The next time you call sdk.blueprints.entitiesOf('todo'), your IDE will know the exact entity shape.

Examples

Generate TypeScript declarations (default)

bash
qelos interfaces build

Given a blueprints/todo.blueprint.json:

json
{
  "identifier": "todo",
  "name": "Todo",
  "properties": {
    "title":     { "type": "string",  "required": true },
    "completed": { "type": "boolean" },
    "status":    { "type": "string",  "required": true, "enum": ["open", "done"] }
  },
  "relations": [
    { "key": "project", "target": "project" }
  ]
}

The generated types/qelos-blueprints.d.ts looks like this:

ts
// AUTO-GENERATED — DO NOT EDIT
// Generated by `qelos interfaces build`
// Source: *.blueprint.json files

import type { IBaseBlueprintEntity } from '@qelos/sdk/blueprints-entities';

export interface TodoProperties {
  title: string;
  completed?: boolean;
  status: "open" | "done";
  project?: Pick<ProjectEntity, 'identifier'> | string;
}

export interface TodoEntity extends IBaseBlueprintEntity, TodoProperties {}

export interface BlueprintEntitiesRegistry {
  "todo": TodoEntity;
}

declare module '@qelos/sdk' {
  interface BlueprintEntitiesRegistry {
    "todo": TodoEntity;
  }
}

Now your SDK calls are fully typed:

ts
import QelosSDK from '@qelos/sdk';

const sdk = new QelosSDK({ appUrl: 'https://my-instance.qelos.app' });

// `todos` is automatically typed as QlBlueprintEntities<TodoEntity> —
// no generic parameter required.
const todos = sdk.blueprints.entitiesOf('todo');

const list = await todos.find();           // TodoEntity[]
const item = await todos.create({
  title: 'Ship interfaces docs',
  status: 'open',                          // ✓
  // status: 'archived',                    // ✗ TS error: not assignable to "open" | "done"
});

await todos.update(item._id, { completed: true });

Generate Python TypedDicts

bash
qelos interfaces build --lang py --out ./types

Produces types/qelos_blueprints.py:

python
# AUTO-GENERATED — DO NOT EDIT
# Generated by `qelos interfaces build --lang py`
# Source: *.blueprint.json files

import datetime
from typing import Any, Dict, List, Literal, TypedDict, Union
from typing_extensions import NotRequired

class TodoProperties(TypedDict):
    title: str
    completed: NotRequired[bool]
    status: Literal["open", "done"]
    project: NotRequired[Union["ProjectEntity", str]]

class TodoEntity(TodoProperties):
    _id: str
    identifier: str

Use the generated TypedDicts with the Python SDK:

python
from qelos_sdk import QelosSDK
from types.qelos_blueprints import TodoEntity

sdk = QelosSDK(app_url="https://my-instance.qelos.app")

todos: list[TodoEntity] = sdk.blueprints.entities_of("todo").find()

Custom blueprint and output paths

bash
qelos interfaces build ./db/blueprints --out ./src/generated

Save defaults for the project

Persist the options you typed into qelos.config.json so the next run doesn't need any flags:

bash
qelos interfaces build --lang ts --out ./src/generated --save
json
{
  "interfaces": {
    "lang": "ts",
    "out": "./src/generated",
    "path": "./blueprints"
  }
}

After saving, just run:

bash
qelos interfaces build

End-to-end demo

A full round-trip from a remote Qelos instance to a typed front-end:

bash
# 1. Pull blueprints from your Qelos instance
qelos pull blueprints ./blueprints

# 2. Generate typed declarations
qelos interfaces build

# 3. Use the typed SDK in your app
cat <<'EOF' > app.ts
import QelosSDK from '@qelos/sdk';

const sdk = new QelosSDK({ appUrl: process.env.QELOS_URL! });

async function main() {
  // `entitiesOf` returns the right entity type from the registry —
  // no manual generic, no casts.
  const products = sdk.blueprints.entitiesOf('product');
  const top = await products.find({ $limit: 10, $sort: '-created' });
  for (const item of top) {
    console.log(item.name, item.price);
  }
}

main();
EOF

# 4. Type-check it
npx tsc --noEmit app.ts

When the blueprint definitions change in Qelos, re-run qelos pull blueprints followed by qelos interfaces build (or wire both into your predev / prebuild script).

CI-friendly script

Add a predev / prebuild hook so types stay fresh:

json
{
  "scripts": {
    "interfaces": "qelos pull blueprints ./blueprints && qelos interfaces build",
    "predev": "npm run interfaces",
    "prebuild": "npm run interfaces"
  }
}

Notes & limitations

  • The command does not require a network connection — it only reads local .blueprint.json files. Use qelos pull blueprints first if you need to refresh from your Qelos instance.
  • Files without an identifier field are skipped with a warning.
  • Property names that aren't valid identifiers (TS) or are Python reserved words (Py) are quoted (TS) or skipped (Py) automatically.
  • Nested object schemas are walked up to 4 levels deep in TypeScript; deeper structures fall back to Record<string, any>.
  • The generated file always overwrites the previous output — never edit it by hand.
  • tsconfig.json discovery is non-recursive (current directory only). For monorepos, run the command from the package that owns the tsconfig.json you want to extend.

Build SaaS Products Without Limits.