@datafn/cli
v0.0.2
Published
CLI and Codegen for DataFn
Readme
@datafn/cli
CLI tools and TypeScript code generation for DataFn. Generates Drizzle ORM schema definitions and typed interfaces from DataFn schemas.
Installation
npm install @datafn/cliFeatures
| Feature | Description |
|---------|-------------|
| Schema Generation | Generate Drizzle ORM schema (pgTable, mysqlTable, sqliteTable) from DataFn schema |
| Type Generation | Generate TypeScript interfaces, Tables map, and TypedClient from DataFn schema |
| Multi-Database | PostgreSQL, MySQL, SQLite — each with dialect-specific Drizzle column types |
| CLI Interface | npx @datafn/cli generate command for schema + type generation |
CLI Usage
generate
Generate Drizzle ORM schema and TypeScript type definitions from a DataFn schema.
# Auto-discover schema file
npx @datafn/cli generate --adapter drizzle --database postgres
# Explicit schema file
npx @datafn/cli generate --adapter drizzle --database postgres --schema ./schema.datafn.ts
# Custom output directory
npx @datafn/cli generate --adapter drizzle --database postgres --output ./src/db/Options
| Option | Description | Default |
|--------|-------------|---------|
| --adapter <name> | ORM adapter (currently only drizzle supported) | Required |
| --database <name> | Database dialect: postgres, mysql, sqlite | Required |
| --schema <path> | Schema file (.json or .datafn.ts). Auto-discovers if omitted | Auto-discover |
| --output <dir> | Output directory for generated files | ./ |
| -h, --help | Show help | — |
Output Files
The generate command produces two files in the output directory:
datafn-schema.drizzle.ts— Drizzle ORM table definitionsdatafn-types.ts— TypeScript type definitions
Example Output: Drizzle Schema (PostgreSQL)
Given this DataFn schema:
// schema.datafn.ts
export default {
resources: [
{
name: 'task',
version: 1,
fields: [
{ name: 'id', type: 'string', required: true },
{ name: 'title', type: 'string', required: true },
{ name: 'done', type: 'boolean', required: false, default: false },
{ name: 'createdAt', type: 'date', required: true },
],
indices: { base: ['createdAt'] },
},
],
relations: [],
};The generated datafn-schema.drizzle.ts will be:
// Generated by @datafn/cli. DO NOT EDIT.
// Database: postgres
import { pgTable, text, boolean, bigint, index } from "drizzle-orm/pg-core";
export const task = pgTable("task", {
createdAt: bigint("created_at", { mode: "number" }).notNull(),
done: boolean("done").default(false),
id: text("id").primaryKey(),
title: text("title").notNull(),
}, (table) => [
index("task_created_at_idx").on(table.createdAt),
]);
export const schema = { task };Auto-Discovery
When --schema is omitted, the CLI searches for schema files in this order:
./schema.datafn.ts./src/schema.datafn.ts./schema.json./src/schema.json
Production Workflow
The recommended workflow for DataFn with generated schemas:
1. Define Your Schema
Write your DataFn schema as the single source of truth:
// schema.datafn.ts
import type { DatafnSchema } from '@datafn/core';
export default {
resources: [
{
name: 'user',
version: 1,
fields: [
{ name: 'id', type: 'string', required: true },
{ name: 'email', type: 'string', required: true, unique: true },
{ name: 'name', type: 'string', required: true },
],
},
],
relations: [],
} satisfies DatafnSchema;2. Generate ORM Schema + Types
datafn generate --adapter drizzle --database postgresThis creates:
./datafn-schema.drizzle.ts— Drizzle table definitions./datafn-types.ts— TypeScript interfaces
3. Generate Migrations
Use your ORM's native migration tools:
# Drizzle
drizzle-kit generate
drizzle-kit migrate4. Use Generated Schema in Your App
import { drizzle } from 'drizzle-orm/node-postgres';
import { schema } from './datafn-schema.drizzle';
import { Pool } from 'pg';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const db = drizzle(pool, { schema });
// Schema is now registered with Drizzle5. Create DataFn Server
import { createDatafnServer } from '@datafn/server';
import { drizzleAdapter } from '@superfunctions/db/adapters';
import { schema as datafnSchema } from './schema.datafn';
const server = await createDatafnServer({
schema: datafnSchema,
db: drizzleAdapter(db),
});Important: When upgrading to a new version of DataFn with schema changes, always:
- Run
datafn generateto update schema files - Run ORM migration tools to apply database changes
- Deploy updated code
TypeScript Schema Support
Overview
The DataFn CLI supports TypeScript schema files for a better developer experience with type safety and IDE autocomplete.
Convention: .datafn.ts Extension
TypeScript schema files must use the .datafn.ts extension:
// schema.datafn.ts
import type { DatafnSchema } from '@datafn/core';
export default {
resources: [
{
name: 'task',
version: 1,
fields: [
{ name: 'id', type: 'string', required: true },
{ name: 'title', type: 'string', required: true },
{ name: 'done', type: 'boolean', required: false },
],
},
],
relations: [],
} satisfies DatafnSchema;Why .datafn.ts?
- Security: Prevents accidental execution of arbitrary TypeScript files
- Clarity: Signals that the file contains a DataFn schema
- Convention: Similar to
*.config.ts,*.spec.ts, etc.
Auto-Discovery
When you run datafn generate without the --schema flag, the CLI automatically searches for schema files in this order:
./schema.datafn.ts(current directory)./src/schema.datafn.ts(source directory)./schema.json(JSON schema file)./src/schema.json(JSON schema file in src)
The first file found is used.
Export Formats
Your .datafn.ts file must export the schema in one of these ways:
Default export (recommended):
export default { resources: [...], relations: [...] };Named export:
export const schema = { resources: [...], relations: [...] };Security Best Practices
⚠️ Important: .datafn.ts files are executed during import. Follow these guidelines:
✅ Do:
- Define schema as a plain object literal
- Import types from
@datafn/core - Use simple object/array compositions
❌ Don't:
- Make network requests
- Perform file system operations
- Execute side effects in module scope
- Import runtime dependencies unnecessarily
Good Example:
import type { DatafnSchema } from '@datafn/core';
export default {
resources: [/* ... */],
relations: [],
} satisfies DatafnSchema;Bad Example:
import axios from 'axios'; // ❌ Runtime dependency
const data = await axios.get('/api/schema'); // ❌ Side effect
export default data.schema;Migrating from JSON to TypeScript
To convert an existing JSON schema to TypeScript:
- Rename
schema.jsontoschema.datafn.ts - Add type import:
import type { DatafnSchema } from '@datafn/core'; - Add export:
export defaultbefore the schema object - Add type annotation:
satisfies DatafnSchemaafter the object - Remove quotes from unquoted JSON keys if desired
Before (schema.json):
{
"resources": [
{
"name": "task",
"version": 1,
"fields": [...]
}
],
"relations": []
}After (schema.datafn.ts):
import type { DatafnSchema } from '@datafn/core';
export default {
resources: [
{
name: 'task',
version: 1,
fields: [...]
}
],
relations: []
} satisfies DatafnSchema;Troubleshooting
Error: "Invalid schema file extension"
- Cause: Trying to use
.tsfile without.datafn.convention - Solution: Rename to
*.datafn.ts
Error: "No schema export found"
- Cause: File doesn't export schema
- Solution: Add
export default { ... }orexport const schema = { ... }
Error: "Failed to load TypeScript schema" (Node.js)
- Cause:
tsxpackage not installed - Solution: Run
npm install tsx --save-devor use Bun
Types don't match expectations
- Cause: Schema validation failed
- Solution: Check console output for validation errors from
validateSchema()
Runtime Requirements
- Bun: Native TypeScript support (no additional deps)
- Node.js: Requires
tsxpackage (npm install tsx --save-dev)
Programmatic API
generateTypes(schema): string
Generate TypeScript definitions from a DataFn schema. The schema is validated first; invalid schemas throw a DatafnError.
import { generateTypes } from "@datafn/cli";
const code = generateTypes({
resources: [
{
name: "task",
version: 1,
fields: [
{ name: "id", type: "string", required: true, unique: true },
{ name: "title", type: "string", required: true },
{ name: "done", type: "boolean", required: false },
{ name: "priority", type: "number", required: false },
{ name: "dueDate", type: "date", required: false },
{ name: "metadata", type: "object", required: false },
{ name: "tags", type: "array", required: false },
],
},
{
name: "project",
version: 1,
fields: [
{ name: "id", type: "string", required: true, unique: true },
{ name: "name", type: "string", required: true },
],
},
],
});
console.log(code);Generated output:
import type { DatafnClient, DatafnTable } from "@datafn/client";
export interface Project {
id: string;
name: string;
}
export interface Task {
done?: boolean;
dueDate?: number;
id: string;
metadata?: Record<string, unknown>;
priority?: number;
tags?: unknown[];
title: string;
}
export interface Tables {
project: Project;
task: Task;
}
export type TypedClient = DatafnClient & {
project: DatafnTable<Project>;
task: DatafnTable<Task>;
};Type mapping:
| DataFn Type | TypeScript Type |
|-------------|----------------|
| string | string |
| number | number |
| boolean | boolean |
| object | Record<string, unknown> |
| array | unknown[] |
| date | number (timestamp) |
| file | string (URL or ID) |
Resources and fields are sorted alphabetically for deterministic output.
generateDrizzleSchema(schema, dialect): string
Generate Drizzle ORM table definitions from a DataFn schema.
import { generateDrizzleSchema } from "@datafn/cli";
const code = generateDrizzleSchema(
{
resources: [
{
name: "task",
version: 1,
fields: [
{ name: "id", type: "string", required: true },
{ name: "title", type: "string", required: true },
{ name: "done", type: "boolean", default: false },
{ name: "createdAt", type: "date", required: true },
],
indices: { base: ["createdAt"] },
},
],
relations: [],
},
"postgres"
);
console.log(code);Generated output:
// Generated by @datafn/cli. DO NOT EDIT.
// Database: postgres
import { pgTable, text, boolean, bigint, index } from "drizzle-orm/pg-core";
export const task = pgTable("task", {
createdAt: bigint("created_at", { mode: "number" }).notNull(),
done: boolean("done").default(false),
id: text("id").primaryKey(),
title: text("title").notNull(),
}, (table) => [
index("task_created_at_idx").on(table.createdAt),
]);
export const schema = { task };Supported dialects:
"postgres"— PostgreSQL (pgTable)"mysql"— MySQL (mysqlTable)"sqlite"— SQLite (sqliteTable)
Type mapping:
PostgreSQL:
| DataFn Type | Drizzle Column |
|-------------|---------------|
| string | text("field") |
| number | numeric("field") |
| boolean | boolean("field") |
| date | bigint("field", { mode: "number" }) |
| object | jsonb("field") |
| array | jsonb("field") |
| file | text("field") |
MySQL:
| DataFn Type | Drizzle Column |
|-------------|---------------|
| string | text("field") |
| number | int("field") |
| boolean | boolean("field") |
| date | bigint("field", { mode: "number" }) |
| object | json("field") |
| array | json("field") |
| file | text("field") |
SQLite:
| DataFn Type | Drizzle Column |
|-------------|---------------|
| string | text("field") |
| number | integer("field") |
| boolean | integer("field") |
| date | integer("field") |
| object | text("field") |
| array | text("field") |
| file | text("field") |
Features:
- Automatic
idfield detection (.primaryKey()) - Field modifiers:
.notNull(),.unique(),.default(value) - Index generation from
indices.base - Join table generation for many-many relations
- camelCase → snake_case column naming
Exports
// Schema generation
export { generateDrizzleSchema }
// Type generation
export { generateTypes }
// Types
export type { DatafnSchema } from '@datafn/core'License
MIT
