Generate type-safe Effect Schema class definitions from your Tana supertags. This enables building applications with full type safety based on your Tana workspace structure.
# Generate schemas for all supertags
supertag codegen generate -o ./generated/schemas.ts
# Generate for specific supertags only
supertag codegen generate -o ./generated/todo.ts --tags Todo Meeting
# Preview without writing files
supertag codegen generate -o ./generated/schemas.ts --dry-runGenerate Effect Schema classes from supertag definitions.
supertag codegen generate -o <output-path> [options]Required:
-o, --output <path>- Output file path
Options:
-t, --tags <tags...>- Filter to specific supertag names--split- Generate separate file per supertag (creates directory structure)--optional <strategy>- How to handle optional fields:option(default),undefined, ornullable--no-metadata- Exclude supertag metadata comments from output-d, --dry-run- Preview generated code without writing files-w, --workspace <name>- Use specific workspace (default: main)
supertag codegen generate -o ./schemas.tsGenerates all supertags in one file:
/**
* Generated by supertag-cli codegen
* Source: main workspace
* Generated: 2025-12-28T12:00:00.000Z
*/
import { Schema } from "effect";
/**
* Todo supertag
* Fields: 4
*/
export class Todo extends Schema.Class<Todo>("Todo")({
id: Schema.String,
title: Schema.optionalWith(Schema.String, { as: "Option" }),
dueDate: Schema.optionalWith(Schema.DateFromString, { as: "Option" }),
completed: Schema.optionalWith(Schema.Boolean, { as: "Option" }),
priority: Schema.optionalWith(Schema.String, { as: "Option" }),
}) {}supertag codegen generate -o ./schemas/index.ts --splitCreates a directory structure:
schemas/
├── index.ts # Re-exports all schemas
├── Todo.ts # Todo schema
├── Meeting.ts # Meeting schema
└── Person.ts # Person schema
Tana field types are mapped to Effect Schema types:
| Tana Type | Effect Schema | Notes |
|---|---|---|
text |
Schema.String |
Default for unknown types |
number |
Schema.Number |
Numeric values |
date |
Schema.DateFromString |
Parses ISO date strings |
checkbox |
Schema.Boolean |
True/false values |
url |
Schema.String.pipe(Schema.pattern(/^https?:\/\//)) |
URL validation |
email |
Schema.String |
Email addresses |
reference |
Schema.String |
Node ID references |
options |
Schema.String |
Option field values |
Control how optional fields are represented:
supertag codegen generate -o ./schemas.ts --optional option// Uses Effect's Option type - recommended for Effect-based apps
title: Schema.optionalWith(Schema.String, { as: "Option" }),supertag codegen generate -o ./schemas.ts --optional undefined// Standard TypeScript optional - simpler but less type-safe
title: Schema.optional(Schema.String),supertag codegen generate -o ./schemas.ts --optional nullable// Allows null values - for APIs that use null
title: Schema.optionalWith(Schema.String, { nullable: true }),Supertags that extend other supertags generate proper class inheritance:
// Base supertag
export class Task extends Schema.Class<Task>("Task")({
id: Schema.String,
title: Schema.optionalWith(Schema.String, { as: "Option" }),
status: Schema.optionalWith(Schema.String, { as: "Option" }),
}) {}
// Child supertag extends parent
export class WorkTask extends Task.extend<WorkTask>("WorkTask")({
project: Schema.optionalWith(Schema.String, { as: "Option" }),
assignee: Schema.optionalWith(Schema.String, { as: "Option" }),
}) {}import { Schema } from "effect";
import { Todo } from "./schemas";
// Decode unknown data
const decodeTodo = Schema.decodeUnknown(Todo);
const result = decodeTodo({
id: "abc123",
title: "Buy groceries",
completed: false,
});
// result is Effect<Todo, ParseError>import { Effect, Either } from "effect";
import { Todo } from "./schemas";
const program = Effect.gen(function* () {
const todo = yield* Schema.decode(Todo)({
id: "abc123",
title: "Important task",
priority: "high",
});
console.log(todo.title); // Option<string>
return todo;
});
Effect.runPromise(program);import { Schema } from "effect";
import { Todo } from "./schemas";
// Extract the decoded type
type TodoType = Schema.Schema.Type<typeof Todo>;
// Extract the encoded type (for serialization)
type TodoEncoded = Schema.Schema.Encoded<typeof Todo>;See the TUI Todo example for a complete application demonstrating:
- Generated schemas from Tana supertags
- SQLite database queries using supertag-cli indexed data
- Creating new nodes via Tana Input API
- Terminal UI with Ink (React for CLIs)
cd examples/tui-todo
bun install
bun run startThe codegen feature reads from the supertag-cli database, which is populated by:
# Export from Tana
supertag-export run
# Index the export
supertag sync indexSupertag definitions including field types and inheritance are extracted during indexing and stored in the supertag_metadata and supertag_fields tables.
-
Regenerate after schema changes: When you modify supertags in Tana, re-export and regenerate schemas.
-
Use version control: Commit generated schemas to track changes over time.
-
Consider split mode for large workspaces: With many supertags, split mode keeps files manageable.
-
Match optional strategy to your codebase: Use
optionfor Effect-heavy code,undefinedfor simpler TypeScript. -
Filter to relevant supertags: Use
--tagsto generate only what you need.
Ensure you've indexed your Tana export:
supertag sync indexFields with unrecognized types default to Schema.String. This is safe but may not validate as expected.
Run with --dry-run first to preview the output. Check for:
- Reserved TypeScript keywords in field names
- Special characters in supertag names
- Circular inheritance (not supported)