A codegen pipeline fetches each endpoint’s OpenAPI schema from fal.ai, converts it to a manifest, and the runtime loads node classes from that manifest. FAL nodes are not hand-written.

Runs: cloud. Needs: FAL_API_KEY. Provider calls throw FAL_API_KEY is not configured without it. Codegen only needs the key to refresh pricing bundles.

Steps

  1. Add the endpoint ID + NodeConfig to the right src/configs/<category>.ts file in packages/fal-codegen/.
  2. Run npm run generate:fal from the repo root. To fetch only endpoints the manifest lacks, run npx tsx scripts/append-new-endpoints.ts from packages/fal-codegen/.
  3. Run npm run build:packages (fal-nodes loads from dist/).
  4. Run npm run lint --workspace=packages/fal-codegen, npm run test --workspace=packages/fal-nodes, and npm run generate:fal:check.
  5. Never commit manual edits to fal-manifest.json.

Where things live

Concern Path
Endpoint configs (source of truth) packages/fal-codegen/src/configs/<category>.ts
Config type definitions packages/fal-codegen/src/types.ts
Schema fetcher (caches to .codegen-cache/) packages/fal-codegen/src/schema-fetcher.ts
Schema → NodeSpec parser packages/fal-codegen/src/schema-parser.ts
Config overrides applier packages/fal-codegen/src/node-generator.ts
Codegen entry point packages/fal-codegen/src/generate.ts
Generated manifest (do not edit) packages/fal-nodes/src/fal-manifest.json
Runtime node class factory packages/fal-nodes/src/fal-factory.ts
Node utilities: endpoint call and upload through the provider, asset conversion packages/fal-nodes/src/fal-base.ts
Package entry point packages/fal-nodes/src/index.ts
Pricing bundle (generated) packages/fal-nodes/src/generated/fal-node-type-pricing.json
Provider: queue, upload and API key for capability calls and node endpoint calls packages/runtime/src/providers/fal-provider.ts
Manifest model loading packages/runtime/src/providers/manifest-models.ts

How FAL nodes are generated

src/configs/<category>.ts   ← you edit this
        ↓
schema-fetcher.ts           fetches https://fal.ai/api/openapi/queue/openapi.json?endpoint_id=...
                            caches each schema in packages/fal-codegen/.codegen-cache/<sha>.json
        ↓
schema-parser.ts            converts OpenAPI → NodeSpec (inputFields, outputType, enums)
        ↓
node-generator.ts           merges NodeConfig overrides (className, fieldOverrides, enumOverrides…)
        ↓
generate.ts                 writes packages/fal-nodes/src/fal-manifest.json
        ↓
fal-factory.ts              creates runtime node classes from manifest at startup

The root generate:fal script runs in strict mode by default: if any configured endpoint cannot be fetched or parsed, generation fails. Remove stale endpoints from the config or fix them before regenerating.

Never edit fal-manifest.json directly. Changes are overwritten on the next npm run generate:fal.

Add a new FAL endpoint

1. Find the right config file

Pick the file in packages/fal-codegen/src/configs/ that matches the endpoint’s modality:

Endpoint type Config file
text → image text-to-image.ts
image → video image-to-video.ts
text → video text-to-video.ts
text → speech text-to-speech.ts
image → image image-to-image.ts
other matching <input>-to-<output>.ts, or unknown.ts

2. Add a NodeConfig entry

Open the chosen config file and add a key inside configs whose value is a NodeConfig:

// packages/fal-codegen/src/configs/text-to-image.ts
import type { ModuleConfig } from "../types.js";

export const config: ModuleConfig = {
  configs: {
    // ... existing entries ...

    "fal-ai/my-new-model": {
      className: "MyNewModel",
      docstring: "One sentence describing what the model does.",
      tags: ["image", "generation", "text-to-image", "txt2img"],
      useCases: [
        "Generate images from text descriptions",
        "Create concept art from prompts"
      ],
      fieldOverrides: {
        prompt: {
          description: "The text prompt to generate an image from"
        },
        image_size: {
          propType: "enum",
          enumRef: "ImageSizePreset",   // reuse a sharedEnum if available
          default: "landscape_4_3",
          description: "Output image size preset"
        },
        seed: {
          propType: "int",
          default: -1,
          description: "Seed for reproducible results. Use -1 for random"
        }
      }
    }
  }
};

NodeConfig fields (all optional — only add what needs overriding):

Field Type Purpose
className string PascalCase class name for the node
docstring string Node description shown in the UI
tags string[] Used for node search
useCases string[] Shown in node detail panel
fieldOverrides Record<string, Partial<FieldDef>> Override parsed field properties (type, default, description…)
enumOverrides Record<string, string> Rename enums: { ImageSize: "ImageSizePreset" }
enumValueOverrides Record<string, Record<string, string>> Rename enum values

Common FieldDef override keys: propType, default, description, enumRef, min, max. The full field list, including acceptsObject and nestedAssetKey, is in packages/fal-codegen/src/types.ts.

Valid propType values: "str" "float" "int" "bool" "image" "video" "audio" "enum" "list[image]" "list[video]" "list[audio]" "dict[str, any]".

3. (Optional) Add a shared enum

If the endpoint introduces a new enum that other nodes in the same module will reuse, add it to the sharedEnums block:

export const config: ModuleConfig = {
  sharedEnums: {
    MyAspectRatio: {
      name: "MyAspectRatio",
      values: [
        ["SQUARE", "1:1"],
        ["WIDESCREEN", "16:9"],
        ["PORTRAIT", "9:16"]
      ],
      description: "Aspect ratio for output"
    }
  },
  configs: { /* ... */ }
};

Then reference it via enumRef: "MyAspectRatio" in a fieldOverrides entry.

4. Run codegen

# From the repo root:
npm run generate:fal

This fetches the schema (or reads from .codegen-cache/ if cached), writes packages/fal-nodes/src/fal-manifest.json, and updates pricing bundles under packages/fal-nodes/src/generated/ when FAL_API_KEY is set. Without the key, existing pricing bundles stay untouched, or empty stubs are written when none exist. Pass --no-pricing to skip the pricing step and its warning.

To force a fresh schema fetch (bypass cache):

npm run generate --workspace=packages/fal-codegen -- --strict --no-cache

Verify

Run these in order after any config change:

# 1. Regenerate the manifest
npm run generate:fal

# 2. Check for TypeScript errors in codegen
npm run lint --workspace=packages/fal-codegen

# 3. Run codegen tests and the fixture drift gate
npm run test --workspace=packages/fal-codegen
npm run generate:fal:check

# 4. Build fal-nodes (loads manifest from dist/)
npm run build:packages

# 5. Run fal-nodes tests (exercises the generated node classes)
npm run test --workspace=packages/fal-nodes

# 6. Smoke-test one generated node (replace type and props as appropriate)
npm run dev:nodetool -- node run fal.text_to_image.FluxDev \
  --props '{"prompt": "a red apple on a white table"}' \
  --no-secrets

The node type follows the pattern fal.<module_name>.<ClassName>, where module_name comes from the config file key in src/configs/index.ts (e.g. text_to_image).

Before committing, inspect the manifest diff:

git diff packages/fal-nodes/src/fal-manifest.json | head -80

Confirm the new entry appears with the expected className, inputFields, and outputType.

Parser and codegen bugs go in packages/fal-codegen/. Runtime bugs go in packages/fal-nodes/. Never patch fal-manifest.json.

Fixing a wrong input or output on a generated node

Problem Where to fix
Field parsed with wrong type packages/fal-codegen/src/schema-parser.ts
Field needs a different default or description fieldOverrides in the endpoint’s config entry
Enum name conflicts across nodes enumOverrides in the config entry
Asset input defaults to "" instead of AssetRef defaultForPropType() in packages/node-sdk/src/manifest-node-values.ts
Audio/video output missing preview set metadataOutputTypes in the factory, not outputTypes
Queue, upload or progress behavior packages/runtime/src/providers/fal-provider.ts
Input conversion or output mapping packages/fal-nodes/src/fal-base.ts, fal-factory.ts