Getting Started
Status
Not yet published to npm. Install from the monorepo (git or workspace link) until packages ship — see Status.
Install
The core packages are separate npm packages, composed via workspace/git dependency until publish:
bun add @rhi-zone/fractal-api-tree
bun add @rhi-zone/fractal-http-api-projector # HTTP + OpenAPI + typed client
bun add @rhi-zone/fractal-type-ir # type projections (Zod, JSON Schema, SQL DDL, ...)Add other projector packages as needed: @rhi-zone/fractal-mcp-api-projector, @rhi-zone/fractal-cli-api-projector, @rhi-zone/fractal-graphql-api-projector, @rhi-zone/fractal-json-rpc-api-projector.
Author a tree
The unit of composition is a plain data tree, not a registered router. api() builds a branch, op() builds a leaf:
import { api, op } from "@rhi-zone/fractal-api-tree"
import { http } from "@rhi-zone/fractal-http-api-projector/verbs"
const books = new Map<string, { id: string; title: string; author: string }>()
const tree = api({
books: api({
list: op(() => [...books.values()], http.get),
add: op((input: { title: string; author: string }) => {
const id = `book-${books.size + 1}`
const book = { id, ...input }
books.set(id, book)
return book
}, http.post),
}),
})op(fn, ...contributions) attaches metadata (verb bundles, tags, custom fields) to a handler. api(children, opts?) groups nodes into a branch. Both return the same Node value — projections dispatch on node.handler vs node.children, not on a wrapper type.
Project to HTTP
import { createFetch } from "@rhi-zone/fractal-http-api-projector"
const fetch = createFetch(tree)
const res = await fetch(new Request("http://localhost/books/list"))
await res.json() // []
const created = await fetch(new Request("http://localhost/books/add", {
method: "POST",
body: JSON.stringify({ title: "Dune", author: "Herbert" }),
}))OpenAPI 3.1 is served for free at GET /openapi.json — createFetch builds and caches it lazily from the live route tree.
Project the same tree elsewhere
import { toTools } from "@rhi-zone/fractal-mcp-api-projector"
const tools = toTools(tree)
// [{ name: "books_list", ... }, { name: "books_add", ... }]
import { runCli } from "@rhi-zone/fractal-cli-api-projector"
await runCli(tree, ["books", "add", "--title", "Dune", "--author", "Herbert"])No re-description: the same tree value drives the HTTP router, the OpenAPI document, the MCP tool list, and the CLI. See the Framework reference for HTTP/MCP/CLI/GraphQL/JSON-RPC in depth.
Your first type projection
Independent of the API tree, @rhi-zone/fractal-type-ir projects a TypeRef — a small subtyping IR — to 20+ target languages/formats:
import { t, types } from "@rhi-zone/fractal-type-ir"
import { toZod } from "@rhi-zone/fractal-type-ir/zod"
import { toPython } from "@rhi-zone/fractal-type-ir/python-dataclass"
const book = t(types.object({
id: t(types.string),
title: t(types.string),
year: t(types.integer),
}))
toZod(book)
// z.object({ id: z.string(), title: z.string(), year: z.number().int() })
toPython(book, "Book")
// @dataclass
// class Book:
// id: str
// title: str
// year: intEvery projector is TypeRef => string (or a small document type for schema-based formats). See the Type-IR reference for the full catalog, grouped by language.
Next
- Concepts — the node/dispatch/tags/metadata model
- Authoring — building trees with
op,service,param, verb bundles - Design philosophy — the biases behind the shape of the library