Skip to main content
Forst plugins generate integration files directly from your typechecked backend services. When you run forst generate, the compiler typechecks your Forst code and projects the resulting type definitions and function signatures into an in-memory snapshot. The compiler then executes your configured plugin binaries and passes this snapshot to them over standard input. Plugins emit generated code—such as JSON Schema files, RPC contract definitions, or framework route handlers—into dedicated output folders. Your application imports these generated outputs, ensuring external contracts and routers stay synchronized with your backend code without manual duplication. Because generated files are written exclusively to designated output directories, running forst generate updates your generated code without overwriting hand-written source files.
Generate plugins are work in progress. The stdin/stdout protocol, official emitters, resolver behavior, and CLI download path are usable for experimentation and early adoption, but they may change without independent plugin semver. Pin your compiler and @forst/cli version; re-run forst generate and read plugin diagnostics after every upgrade.

Work in progress and known limitations

Semantic plugins and the official emitters are under active development. The table below lists current constraints—not a promise of future design. If you rely on generated artifacts in CI or production, pin the compiler, regenerate in CI or commit outputs, and treat plugin warnings as signals to fix before shipping.

Quickstart

Add a plugin to the generate.plugins array in ftconfig.json:
Run forst generate from your terminal:
This creates generated/jsonschema/schema.json alongside your generated TypeScript client. You can now import schema.json directly into your API gateway, validation pipeline, or external documentation tools. Build official plugin binaries from the Forst repo with task build (compiler + plugins land in bin/). GitHub Releases and @forst/cli ship the same plugin binaries next to the compiler; bare cmd names resolve automatically.

Example project

The repo ships a runnable boundary at examples/in/plugins/ that exercises every official plugin from real .ft sources (RPC catalog, file routes, and React Router handlers). See that directory’s README for commands.

Resolving plugin executables (cmd)

Each generate.plugins[].cmd value resolves as follows: When you invoke forst through @forst/cli, the CLI downloads official plugin binaries into the same version cache directory as the compiler only when forst generate runs against an ftconfig.json that lists bare official plugin names in generate.plugins, then prepends that directory to PATH before spawning. Plugin out directories are owned exclusively by the plugin. On each run, forst generate removes stale files under out that the plugin did not emit. Diagnostics with severity: "error" fail the generate command; warnings are logged only. Invalid opt JSON in ftconfig.json fails the plugin at runtime with a parse error.

What you can build with plugins

Plugins bridge Forst’s type system with any external system or toolchain. Because plugins receive fully resolved types, constraint chains, and function signatures, you can automate any downstream output that derives from backend service definitions:
  • Validation and data schemas. Convert type constraint chains into JSON Schema, Zod, Effect Schema, or custom validation rules for external clients, API gateways, and message queues.
  • API contracts and specifications. Derive end-to-end typed contract trees, OpenAPI specs, AsyncAPI definitions, GraphQL schemas, or client SDKs for RPC frameworks, REST endpoints, and message brokers.
  • Framework and HTTP routing. Generate route tables, parameter mappers, middleware hooks, and request dispatchers for web servers and full-stack frameworks.
  • Full-stack data integration. Produce server-side data loaders, resource route handlers, or form actions so UI frameworks can fetch Forst data without modifying your frontend source code.
  • Persistence and database definitions. Translate shape types and constraints into database schema migrations, SQL constraint assertions, ORM model mappings, or seed fixtures.
  • Infrastructure and cloud topology. Generate IaC definitions (Terraform, Pulumi, AWS CDK, Kubernetes CRDs) or API gateway policies directly from backend service declarations and provider dependencies.
  • CI/CD and pipeline validation. Produce GitHub Actions workflows, contract compatibility matrices, or breaking-change checkers between git revisions.
  • Automated testing and mock suites. Build deterministic test fixtures, fake service implementations, property-based test generators, or contract-testing suites automatically from service shapes.

Official plugin packages

Official plugins are shipped alongside the compiler as standalone binaries. Each emitter is early-stage; output file names, opt fields, and generated APIs may change while plugin versioning matures.

forst-gen-jsonschema

Generates standard JSON Schema files from Forst shapes and constraint chains.
  • Use case: Share strict type validation with external services, API gateways, or OpenAPI documentation.
  • Key behavior: Maps built-in constraints (Min, Max, HasPrefix, Contains) to schema properties. On maps, Min/Max become minProperties/maxProperties. Emits a warning for runtime type-guard functions like Email() rather than guessing a format string.

forst-gen-orpc

Generates Zod schemas and procedure contracts for oRPC or tRPC applications.
  • Use case: Connect frontend web apps to Forst backend services using end-to-end typed RPC.
  • Key behavior: Reads contract types marked with .Router(). Generates zod.ts, contract.ts, and invoke.ts. Supports stream returns as subscriptions and maps optional query or mutation hints.

forst-gen-file-routes

Generates a sealed HTTP route registry from file paths under a target directory.
  • Use case: Build RESTful HTTP APIs where file layout determines URL structure.
  • Key behavior: Converts paths like app/api/orders/$id.ft into route pattern /api/orders/:id. Generates handler wrappers, path parameter bindings, and a central dispatch() function. Skips .Router() types declared outside opt.routesRoot (for example RPC catalogs in catalog/). Skips HTTP members whose bound function is not runnable (same as the React Router plugin) and emits an error diagnostic.

forst-gen-react-router

Generates React Router and Remix resource routes and server loaders.
  • Use case: Integrate Forst data fetching into React Router v7 or Remix full-stack applications.
  • Key behavior: Generates forstApiRoutes arrays, resource handler modules (loader and action), and helper functions for page modules. Never writes into your app/ UI source folder.

forst-gen-echo

Development-only manifest of type and function ids in the semantic snapshot. Use it to verify generate.plugins[] wiring — not for production artifacts.

How to write a custom plugin

You can write a custom plugin in Go or any language that can read standard input and write standard output.

Why write a custom plugin?

Write a custom plugin when you need to transform Forst type metadata into custom project files. Examples include:
  • Generating database migration scripts or SQL CHECK constraints from type assertions.
  • Emitting GraphQL schema definitions (.graphql) from exported shape types.
  • Producing custom SDKs for internal SDK clients or language runtimes.
  • Generating infrastructure config (Terraform, AWS CDK, Kubernetes manifests) based on service definitions.

Under the hood: The semantic snapshot model

Plugins do not receive raw source tokens or abstract syntax trees (AST). Instead, forst generate typechecks your Forst code and projects a structured JSON payload called the semantic snapshot (the same typecheck also feeds the TypeScript client). The snapshot contains resolved, fully-qualified facts about your codebase:
  • Packages: Module layout, directories, files, and exported type/function IDs.
  • Types: Resolved primitives, shapes, field names, optionality, tags, and ordered constraint chains.
  • Functions: Exported declarations, parameters, synthesized input shapes, return types, and nominal error sets.
Because plugins consume resolved type semantics rather than syntax, your plugin logic remains clean and predictable regardless of how the source code was formatted.

Step 1: Implement the JSON protocol

Plugins read a single JSON request from stdin and write a single JSON response to stdout. Here is a minimal Go plugin using the official forst/internal/genplugin helper package:

Step 2: Register your plugin executable

Build your executable and reference it in your ftconfig.json:
When you run forst generate, the compiler executes ./bin/my-custom-plugin, passes the snapshot, and writes generated/summary/SUMMARY.md.

Inspecting snapshots for debugging

You can inspect the exact JSON snapshot emitted by the compiler without executing any plugins by using the --dump-semantic flag:
This prints the complete GenerateRequest payload to standard output, making it easy to test and debug custom plugin transformations.