Farm.js

Configuration

Use farm.config.ts as the single project control plane for source paths, integrations, docs, KV storage, database clients, deployment, and framework behavior.

Define config

farm.config.ts
import { defineConfig } from "@farm.js/core";

export default defineConfig({
  deploy: {
    target: "vercel",
  },
  docs: {
    entry: "/docs",
  },
  md: {
    expose: ["/", "/pricing"],
    cache: 60,
  },
  mdx: {
    components: "./src/markdown-components.tsx",
  },
  theme: {
    default: "system",
  },
});

srcDir defaults to "src". Set it only when the application source lives somewhere else.

defineConfig is the canonical Farm helper. defineFarmConfig remains available as a deprecated exact alias for existing applications.

TypeScript

Farm transpiles TypeScript through the selected renderer and Vite. It reads the project's normal tsconfig.json, but farm build does not run a separate project type check. Keep type checking as an explicit script so the same command runs locally and in CI:

package.json
{
  "scripts": {
    "type-check": "tsc --noEmit"
  }
}

A top-level typescript.tsconfigPath or typescript.ignoreBuildErrors setting has no effect in Farm. Configure compiler behavior in tsconfig.json; point an explicit tsc -p command at a different file when needed.

Renderer

React remains the default renderer, so existing applications and configurations do not need to change. Select another renderer when you want to author the UI with that library while keeping FARMJS routing and server features. See Renderers for the feature matrix and dedicated React, Preact, Solid, Vue, and Svelte guides.

Preact

Install Preact and its FARMJS renderer adapter:

pnpm add @farm.js/preact@beta preact
import { defineConfig } from "@farm.js/core";
import { preact } from "@farm.js/preact";

export default defineConfig({
  renderer: preact(),
});

Preact routes use .tsx or .jsx. The adapter configures Preact JSX, Prefresh, React compatibility aliases, server rendering and streaming, and browser hydration. See the Preact Renderer guide for typed server calls and compatibility boundaries.

Create a ready-to-run Preact application from the CLI:

pnpm --config.minimumReleaseAge=0 create @farm.js/app@beta my-preact-app --template basic --renderer preact --typescript

Svelte

Install the Svelte adapter and runtime:

pnpm add @farm.js/svelte@beta svelte
import { defineConfig } from "@farm.js/core";
import { svelte } from "@farm.js/svelte";

export default defineConfig({
  renderer: svelte(),
});

Routes can then use Svelte 5 components directly:

src/app/layout.svelte
src/app/page.svelte
src/app/products/[id]/page.svelte

See Svelte Renderer for module route exports, layout snippets, hydration, typed server calls, and current compatibility boundaries.

Create a ready-to-run Svelte application from the CLI:

pnpm --config.minimumReleaseAge=0 create @farm.js/app@beta my-svelte-app --template basic --renderer svelte --typescript

Vue

Install Vue and its FARMJS renderer adapter:

pnpm add @farm.js/vue@beta vue
import { defineConfig } from "@farm.js/core";
import { vue } from "@farm.js/vue";

export default defineConfig({
  renderer: vue(),
});

Routes can then use Vue Single-File Components directly:

src/app/layout.vue
src/app/page.vue
src/app/products/[id]/page.vue

FARMJS compiles the SFCs with Vue's Vite plugin, renders them with createSSRApp and renderToString, and hydrates interactive routes in the browser. Layout children are exposed through Vue's default <slot />. See the Vue server-rendering guide for Vue-specific SSR constraints.

See Vue Renderer for SFC route exports, hydration, typed server calls, and current compatibility boundaries.

Create a ready-to-run Vue application from the CLI:

pnpm --config.minimumReleaseAge=0 create @farm.js/app@beta my-vue-app --template basic --renderer vue --typescript

Solid

Install the Solid adapter and runtime:

pnpm add @farm.js/solid@beta solid-js
import { defineConfig } from "@farm.js/core";
import { solid } from "@farm.js/solid";

export default defineConfig({
  renderer: solid(),
});

The renderer controls component compilation, server rendering, and browser hydration. FARMJS continues to own routing, layouts, API routes, middleware, data access, observability, and deployment, so those server features use the same APIs with every renderer. UI code uses the selected library's native primitives—for example, Solid signals instead of React hooks.

See Solid Renderer for route conventions, client boundaries, typed server calls, and current compatibility boundaries.

Create a ready-to-run Solid application directly from the CLI:

pnpm --config.minimumReleaseAge=0 create @farm.js/app@beta my-solid-app --template basic --renderer solid --typescript

Omitting renderer selects React. The renderer option is currently available for the Basic starter; integration starters continue to use React while their UI packages are migrated individually.

Docs config

Configure the docs runtime directly in farm.config.ts:

import { defineConfig } from "@farm.js/core";

export default defineConfig({
  docs: {
    entry: "/docs",
    metadata: {
      description: "Product guides and API reference.",
    },
    nav: {
      title: "Acme Docs",
    },
    search: {
      provider: "simple",
      enabled: true,
    },
    pageActions: {
      copyMarkdown: {
        enabled: true,
      },
    },
    llmsTxt: true,
    sitemap: true,
    robots: true,
  },
});

This single property enables human-readable pages, markdown mirrors, search metadata, and agent-readable docs routes. A separate docs.config.* or docs.json file is optional and intended only for large serializable configurations; inline values always take priority. See Docs Engine for content layout, generated routes, and API overrides.

The same search option configures the search provider and the docs interface. When it is enabled, Farm mounts the shared Omni React search from @farming-labs/theme. The sidebar control and Cmd+K on macOS or Ctrl+K elsewhere open the same search interface used by the other Farming Labs framework adapters. Set search: false or search.enabled: false to remove the control, client mount, and shortcut together.

Important options

OptionUse it for
extendsComposing local or package Farm layers with project-first overrides.
srcDirChanging the app source folder from the default src.
rendererSelecting React (default) or an adapter such as Preact, Svelte, Vue, or Solid.
apiConfiguring the public root used by Farm's typed browser API client.
integrationsRegistering built-in or custom integrations.
authEnabling Farm's built-in email/password auth, sessions, helpers, and hooks.
themeEnabling light, dark, and system modes with client and server APIs.
storageConfiguring KV drivers/mounts and, in the current beta, an integration DB client.
migrationsRunning one-shot schema/provider commands with farm migrate.
cronMapping portable UTC schedules to ordinary GET API routes.
i18nConfiguring locale routes, detection, message catalogs, typing, and direction.
docsServing the built-in docs runtime and docs API.
mdRestricting or disabling automatic markdown mirrors like /pricing.md.
mdxRendering page.md and page.mdx app routes, plus MDX components.
telemetryControlling automatic production-site reporting to Farm's usage dashboard.
deploySelecting a target, preset, and output directory.
deploymentIdDetecting stale browser requests during rolling deployments.
trailingSlashChoosing the canonical URL shape for application page routes and links.
routeRulesApplying rendering, cache, redirect, CORS, and header behavior to route patterns.
securityApplying an app-wide CSP with an enforcing or report-only response header.
serverActionsRestricting trusted action origins and request body size.
imagesConfiguring responsive widths, remote allowlists, formats, and optimizer limits.
performanceBudgeting image and font preload hints without changing the rendered resources.
experimentalAuditing or enabling opt-in rendering experiments such as isolated hydration and PPR.
openapiPublishing API reference docs.

Application base path

Set basePath when the complete application is mounted below the origin root. Farm applies the canonical path consistently to routes, links, assets, and runtime endpoints:

farm.config.ts
export default defineConfig({
  basePath: "/console",
});

Farm accepts a leading-slash or bare pathname and removes duplicate and trailing slashes. It rejects URLs, query strings, hashes, backslashes, control characters, and . or .. segments because a browser could otherwise resolve a different path than Farm's server router.

Trailing slashes

Application page URLs omit a trailing slash by default. Set trailingSlash: true to generate framework links with a slash and redirect matching page requests to that canonical URL in both development and production:

farm.config.ts
export default defineConfig({
  trailingSlash: true,
});

The redirect uses status 308 and preserves the query string. The root URL remains /, and API, integration, image, and metadata routes keep their own URL contracts. A <Link trailingSlash={false}> or <Link trailingSlash> prop overrides the app default for that link.

API client base URL

Farm's typed API client uses the current origin and /api by default. Configure api when the browser should call a different origin or path:

import { defineConfig } from "@farm.js/core";

export default defineConfig({
  api: {
    baseURL: ({ mode }) =>
      process.env.GITHUH_API_URL ?? (mode === "development" ? "http://127.0.0.1:8080" : undefined),
    basePath: "/api",
  },
});

An origin-only baseURL, such as https://api.example.com, is joined with basePath. If baseURL already contains a path, such as https://api.example.com/v1, that path is the API root and basePath is ignored. Both fields accept a string or a sync/async resolver receiving { root, mode, env }. Farm resolves the function during configuration and only embeds the resulting public URL in the browser bundle.

A root-relative API root is also mounted by Farm in development and production. For example, api: { basePath: "/v2/api" } makes a route declared at app/api/users/route.ts available at /v2/api/users. Farm treats an absolute baseURL as external and does not remount the current application's API routes for it. basePath rejects backslashes, control characters, and . or .. segments, including encoded path separators. Root-relative baseURL and basePath values must begin with one slash; network-path references such as //api.example.com are rejected so the server mount and browser URL cannot resolve to different origins or paths.

The option configures the HTTP apiClient returned by createApiClients() automatically (and the existing createAPIClient() factory). The paired server api always uses the local app's registered routes and server mount, never an external browser API origin. An explicit per-client baseURL still takes precedence.

This also applies to RSC builds and their Nitro servers, including apps configured with defineConfig from @farm.js/plugin/rsc. API requests at the custom prefix stay on the API pipeline rather than being decoded as server actions. The canonical /api routes remain available; an external API URL changes the client destination only, not the local server mount.

Isolated client hydration

React applications can keep using "use client" without enabling React Server Components. By default, Farm preserves its compatible route-wide hydration behavior. The isolated hydration experiment lets an otherwise server-rendered page or layout ship and hydrate eligible client leaves instead of the complete route module:

import { defineConfig } from "@farm.js/core";

export default defineConfig({
  experimental: {
    isolatedClientHydration: "enabled",
  },
});

The option has three modes:

ModeBehavior
"off"Keeps route-wide hydration. This is the default.
"analyze"Reports eligible boundaries without changing emitted code or runtime work.
"enabled"Hydrates safe client leaves independently and keeps unsupported routes safe.

An eligible boundary is a local, statically analyzable "use client" module with a default or named capitalized component export and serializable props. Farm preserves its server-rendered HTML, emits the client component as a separate browser chunk, and hydrates that leaf as its own React root. Sibling leaves receive independent roots. Client components imported by another client component stay in their parent's root, so Farm never creates overlapping roots for one client graph. Props travel in an HTML-safe, non-executable JSON payload. Plain objects, arrays, strings, booleans, finite numbers, and null are supported.

Package boundaries, re-export graphs, ambiguous exports, React-element children, functions, symbols, class instances, circular objects, and routes that still require shared React context keep the route-wide path when Farm can identify them statically. If an unsupported value is discovered only while rendering, Farm preserves the SSR output and leaves that boundary inert with a development diagnostic. Module-load and root-render failures likewise restore the original server HTML and report the boundary reference plus the original error.

An integration provider is route-wide by default because an independent root cannot inherit its context. A provider that is safe to instantiate around every isolated root can declare supportsIsolatedHydration: true; otherwise Farm retains route-wide hydration for the app.

Farm also applies a measured graph-cost guard. Up to four statically bounded isolated roots can use the isolated plan. A page or layout with a larger client graph stays on route-wide hydration and prints the owner, detected count, and limit. Lists whose boundary count depends on runtime data also stay route-wide because Farm cannot prove their root cost before streaming. In the maintained 40-sample Chrome browser benchmark, eight independent roots were the first stress shape to exceed the route-wide hydration budget. See the raw samples and full cost table. The same report includes compiler-enabled route-wide and isolated controls. It verifies that every measured leaf actually compiled, then checks initial hydration and repeated state updates independently.

SPA navigation preserves isolated roots that live in a shared layout, including their state and DOM identity. Farm unmounts roots in the outgoing route subtree before replacing it, then hydrates only the boundaries introduced by the incoming fragment. Superseded navigation work is aborted before it can hydrate stale HTML.

This flag does not enable RSC, change the meaning of "use client", or make Server Components part of the wire format. When experimental.serverComponents is enabled, the RSC transport remains the owner and Farm ignores isolated client hydration. Treat "enabled" as an experimental performance option and measure the route's client JavaScript and interaction cost before adopting it broadly. The maintained benchmark includes equivalent RSC controls rather than assuming the non-RSC path is faster.

Partial Prerendering

Partial Prerendering (static-shell caching) is experimental and disabled by default. Enable it app-wide with experimental.ppr, then opt individual routes in with export const ppr = true, the Next-compatible export const experimental_ppr = true, or a "use ppr" directive. Route declarations are inert while the flag is off, and those routes render fully dynamically.

import { defineConfig } from "@farm.js/core";

export default defineConfig({
  experimental: {
    ppr: true,
  },
});

See Cache and PPR for shell caching, Suspense holes, invalidation, and observability events.

Images

Farm optimizes local and allowlisted remote images through the same runtime on development and production deployments.

import { defineConfig } from "@farm.js/core";

export default defineConfig({
  images: {
    remotePatterns: [
      {
        protocol: "https",
        hostname: "images.example.com",
        pathname: "/catalog/**",
      },
    ],
    qualities: [75, 90],
    formats: ["image/avif", "image/webp"],
    maximumResponseBody: "10mb",
  },
});

Remote sources are denied by default. See Images for static imports, responsive layouts, provider selection, caching, and security behavior.

Preload budgets

Farm keeps one image preload—the explicitly high-priority hint first—and two font preloads by default. Lower-priority hints above those budgets are removed from buffered HTML and Link response headers, while the actual image and font elements remain unchanged and load normally. Route scripts, stylesheets, and module preloads are not removed.

import { defineConfig } from "@farm.js/core";

export default defineConfig({
  performance: {
    preload: {
      mode: "enforce",
      maxImages: 1,
      maxFonts: 2,
    },
  },
});

Farm prints one actionable warning when a route exceeds a budget. Use mode: "warn" to audit an existing application without removing any hints. Mark the likely LCP image with preload (or fetchPriority="high") and set preload: false on font declarations that are not needed above the fold.

Layers

Use extends to compose ordinary Farm-shaped directories and packages. Entries apply from left to right, and project files and configuration have final priority.

export default defineConfig({
  extends: ["@company/farm-base", "./layers/commerce"],
});

A layer may contain an optional plain farm.config.ts plus its own src/app, components, middleware, APIs, and programmatic routes. It does not use a separate layer registration function. See Layers for package structure, merge rules, aliases, generated types, and override behavior.

Content Security Policy

Configure an app-wide Content Security Policy under security.csp. Farm applies it to pages, API responses, and pre-rendered output through the same response-header pipeline in development and production.

import { defineConfig } from "@farm.js/core";

export default defineConfig({
  security: {
    csp: {
      directives: {
        defaultSrc: ["'self'"],
        baseUri: ["'self'"],
        objectSrc: ["'none'"],
        frameAncestors: ["'none'"],
        formAction: ["'self'"],
        scriptSrc: ["'self'", "'unsafe-inline'"],
        styleSrc: ["'self'", "'unsafe-inline'"],
        imgSrc: ["'self'", "data:", "blob:"],
        fontSrc: ["'self'", "data:"],
        connectSrc: ["'self'", "https:", "wss:"],
      },
    },
  },
});

Directive names may use camelCase or kebab-case. Farm rejects duplicate normalized names, newlines, and directive values containing semicolons so configuration cannot accidentally create a second policy directive.

Use report-only mode while auditing an existing application:

security: {
  csp: {
    reportOnly: true,
    directives: {
      defaultSrc: ["'self'"],
      reportTo: ["csp-endpoint"],
    },
  },
}

You can also pass an already serialized policy as csp: "default-src 'self'; object-src 'none'". The longer contentSecurityPolicy config name is intentionally unsupported; use csp.

Farm currently emits small inline hydration and route-state bootstraps, so the compatible example allows inline scripts and styles. A stricter policy must supply correct hashes or renderer-generated nonces for every trusted inline bootstrap. Start with reportOnly, inspect violations, and enforce only after the deployed HTML and every third-party integration satisfy the policy.

Server HTTP policy

Farm applies one request-body limit to API routes, integrations, workflow HTTP triggers, and uploads handled by those surfaces. The default is 10 MB.

import { defineConfig } from "@farm.js/core";

export default defineConfig({
  server: {
    bodySizeLimit: "10mb",
    trustProxy: false,
    headersTimeout: "60s",
    requestTimeout: "5m",
    keepAliveTimeout: "5s",
    gracefulShutdownTimeout: "30s",
    health: {
      livenessPath: "/_farm/health/live",
      readinessPath: "/_farm/health/ready",
    },
  },
});

Farm checks Content-Length when present and also counts the received bytes, so chunked requests cannot bypass bodySizeLimit. Oversized requests receive 413 Payload Too Large before the route or integration handler runs. Server Actions keep their separate, tighter serverActions.bodySizeLimit setting.

Body rejection initiates stream cancellation without waiting for producer cleanup. This also applies to cloned requests: an unread original body cannot delay the rejection, and a cleanup failure does not replace the 413 response.

The RSC development bridge applies these limits too. POST, PUT, PATCH, DELETE, and QUERY bodies keep their original bytes, including multipart uploads and binary data. GET and HEAD remain bodyless. Action origin validation runs before buffering an action body.

trustProxy defaults to false. Enable it only when the app is behind a trusted reverse proxy that removes client-supplied forwarding headers and writes its own X-Forwarded-For, X-Forwarded-Host, and X-Forwarded-Proto values. Farm uses those headers for the client address and public request URL only when the proxy is trusted. A directly exposed Farm server must leave it disabled so a client cannot spoof the address or authority used by rate limits, redirects, authentication callbacks, logs, or access policy.

Workflow runner secrets are accepted only through Authorization: Bearer <secret> or X-Farm-Workflow-Secret. Farm does not accept secrets in query strings because URLs are commonly retained in logs, browser history, and referrer data.

The long-running Node adapter applies headersTimeout, requestTimeout, and keepAliveTimeout to its HTTP server. headersTimeout limits how long a client can occupy a connection while sending headers, and requestTimeout limits receipt of the complete request. These are transport timeouts, not limits on route-handler or database execution. Durations accept milliseconds or strings such as "15s", "2m", and "1h".

On SIGTERM or SIGINT, Node output immediately fails readiness, stops accepting connections, drains active responses and streams through Nitro, and then runs Farm integration and plugin cleanup. gracefulShutdownTimeout is the maximum drain period before remaining connections are forced closed. The process starts plugin and integration runtime state before it begins listening, so a successful readiness response means startup completed.

Farm exposes two non-cacheable production health handlers by default:

  • GET /_farm/health/live reports whether the process is alive. It stays successful while the process drains.
  • GET /_farm/health/ready reports whether the instance should receive traffic. It returns 503 before startup completes and after shutdown begins.

Customize both paths through server.health, or set health: false when an adapter supplies its own probes. Long-running Node output guarantees the shutdown sequence. Request-driven serverless and edge environments may not expose a reliable process shutdown event, so cleanup there remains platform-specific and must not be required for data correctness.

Server action security

Server actions are same-origin application RPC endpoints. Farm rejects cross-origin action requests by default and limits the encoded request body to 1 MB.

import { defineConfig } from "@farm.js/core";

export default defineConfig({
  experimental: {
    serverComponents: true,
    serverActions: true,
  },

  serverActions: {
    allowedOrigins: [],
    bodySizeLimit: "1mb",
  },
});

allowedOrigins adds trusted origins when a reverse proxy or multi-origin deployment makes the browser origin differ from the server request origin. Entries can be exact origins, hosts, or leftmost-subdomain wildcards:

serverActions: {
  allowedOrigins: [
    "https://app.example.com",
    "proxy.internal:8443",
    "https://*.preview.example.com",
  ],
}

Do not use allowedOrigins as a replacement for CORS or as a public API allowlist. Browser action requests must provide a matching Origin or Referer; Farm accepts Sec-Fetch-Site: same-origin when both are unavailable. Explicitly configured origins can cross a trusted proxy boundary.

bodySizeLimit accepts bytes or strings such as "500kb", "2mb", and "2MiB". Farm checks Content-Length when present and also counts streamed bytes, so chunked requests cannot bypass the limit.

When streamed action input exceeds the limit, Farm cancels it without awaiting producer cleanup or another branch of a cloned request. Cleanup errors do not replace the action's 413 rejection.

Rejected requests use generic, non-cacheable responses: 403 for origin failures, 413 for oversized bodies, and 415 for unsupported content types. Detailed parsing or execution errors stay in server logs.

Next-style route exports

Farm route modules can expose compact rendering options directly on the page when the behavior belongs to that route.

src/app/blog/page.tsx
export const dynamic = "force-static";
export const revalidate = 60;

export default async function BlogPage() {
  return <main>...</main>;
}

Route rules

Farm's redirects(), rewrites(), and headers() config functions use the same source pattern syntax. :name captures one path segment, while :name* and plain * capture the remaining characters. Redirect and rewrite destinations can reuse named captures or use numbered captures such as $1. Captured path segments are decoded and safely re-encoded before interpolation; empty segments in catch-all captures are removed consistently in development and production. All other source characters are matched literally. Sources must be pathname patterns beginning with /; query strings and hashes belong in redirect or rewrite destinations and are rejected in sources because matching operates on the request pathname. For redirects and rewrites, the incoming query string is preserved when the destination has no query. A query written in the destination replaces the incoming query string. Rewrites use after-files semantics in development and production: an existing Farm page, API, integration, docs, image, or metadata route wins, and the rewrite is considered only as a fallback. Configured response headers are applied after route handlers in both modes, so they win when the same header is returned by a handler. Link is additive: handler and configured link values are merged instead of replacing one another. Set-Cookie is also additive, and each handler or configured cookie remains a separate response header.

export default defineConfig({
  async redirects() {
    return [{ source: "/old/:path*", destination: "/new/:path*", permanent: true }];
  },
});

Redirect statusCode accepts only HTTP redirect statuses 301, 302, 303, 307, or 308. Use permanent: true for the default permanent 308; otherwise Farm defaults to temporary 307. Invalid values fail config resolution instead of producing a non-redirect response with a Location header.

Use routeRules when behavior belongs to a URL pattern instead of one page file. Rules are normalized into Farm redirects/headers and passed to Nitro route rules for production adapters.

import { defineConfig } from "@farm.js/core";

export default defineConfig({
  routeRules: {
    "/": { prerender: true },
    "/blog/**": { swr: 3600 },
    "/admin/**": { render: "dynamic" },
    "/api/**": { cors: true },
    "/assets/**": {
      headers: {
        "Cache-Control": "public, max-age=31536000, immutable",
      },
    },
    "/old": { redirect: "/new" },
  },
});

render: "static" maps to prerendering. render: "dynamic" forces a dynamic response. swr and isr accept true or a TTL in seconds. cors: true applies permissive API CORS headers; pass an object when you need a specific origin, methods, or headers.

Rules can also provide runtime, regions, and maxDuration defaults. File pages, API routes, and layouts can override them with named exports. See Route Runtime for inheritance and deployment behavior.

Prefer route-level exports when one page owns the behavior. Prefer routeRules for broad groups, deployment-facing cache policy, API CORS, static asset headers, and legacy redirects.

Minimal project layout

Farm keeps the base project small:

farm.config.ts
src/
  app/
    page.tsx

Add optional files only when the app needs them:

docs.config.ts              # Optional split for a large docs configuration
docs.json                   # Optional serializable docs configuration
src/app/api/**/route.ts
src/app/**/middleware.ts
src/lib/integrations.ts

Cron in config

Cron entries keep timing policy in farm.config.ts while application work stays in an ordinary API route.

export default defineConfig({
  cron: {
    dailyCleanup: {
      schedule: "0 2 * * *",
      path: "/api/maintenance/cleanup",
    },
  },
});

See Cron for route protection, local commands, UTC syntax, deployment behavior, and reliability boundaries.

Integrations in config

import { defineConfig } from "@farm.js/core";
import { stripe } from "@farm.js/stripe";
import { supabase } from "@farm.js/supabase";

export default defineConfig({
  integrations: {
    billing: stripe({
      secretKey: process.env.STRIPE_SECRET_KEY,
    }),
    auth: supabase({
      url: process.env.SUPABASE_URL,
      anonKey: process.env.SUPABASE_ANON_KEY,
    }),
  },
});

The keys become typed namespaces. billing becomes api.billing, and auth becomes api.auth.

One-shot migrations

Use migrations.commands when the app needs a predictable command before build or deploy. This keeps schema setup close to the database and integration config without turning the framework into a migration engine.

import { defineConfig } from "@farm.js/core";

export default defineConfig({
  migrations: {
    commands: [
      "pnpm drizzle-kit migrate",
      {
        name: "integration schema",
        command: "farm generate --orm sqlite --output ./farm-integrations.sql",
        env: {
          FARM_SCHEMA: "integrations",
        },
      },
    ],
  },
});

Run them with:

farm migrate

Each command runs from the project root unless it sets cwd. Commands run in order and the CLI stops on the first failure.

Production-site telemetry

Farm production server runtimes automatically detect their public HTTPS origin from incoming requests and report it to Farm's production-sites dashboard. No URL configuration is required. To disable this product telemetry for a deployment:

farm.config.ts
export default defineConfig({
  telemetry: false,
});

Farm schedules a small check-in after the first non-health production request and never waits for it before returning the application response. Only the detected origin, Farm version, renderer, and deployment target are sent. See Product telemetry for validation, privacy, retention, preview-environment, opt-out, and static-export details.

Deployment config

export default defineConfig({
  deploy: {
    target: "vercel",
    outputDir: ".vercel/output",
  },
});

deploy.target selects the deployment provider. Farm resolves that to the matching Nitro preset and output shape unless you override it.

Deployment identity

Farm assigns one deployment ID to the server and browser output so requests from an older open page can be detected safely.

export default defineConfig({
  deploymentId: process.env.RELEASE_ID,
});

When deploymentId is omitted, Farm checks FARM_DEPLOYMENT_ID, VERCEL_GIT_COMMIT_SHA, and CF_PAGES_COMMIT_SHA, then calls generateBuildId for production builds. Development uses "development".

For a custom build ID, return one stable value for every instance of the same release:

export default defineConfig({
  generateBuildId: async () => process.env.GIT_SHA || `build-${Date.now()}`,
});

Prefer a CI release or commit identifier when a deployment runs on multiple servers. See Deployment for mismatch behavior.

Production notes

  • Production route discovery disables filesystem watching, including when vite.server.watch is configured. That option still applies to development; a one-shot build does not need a live watcher.
  • Keep secrets in environment variables, not committed config.
  • Use storage.driver and storage.mounts for KV data read through getStorage().
  • Use a raw object at storage.client only when schema-backed integrations need a database client; see Database and ORM Clients.
  • Use migrations.commands for schema setup that should be explicit in CI.
  • Use docs.entry when the docs runtime should be mounted automatically.
  • Prefer route-level exports such as dynamic, revalidate, and ppr when behavior belongs to one page.
  • Prefer routeRules for broad URL patterns and platform-level cache/header behavior.
  • Keep serverActions.allowedOrigins empty unless the deployment has a known proxy-origin mismatch.
  • Give every rolling release one stable deploymentId; do not generate a different value per server instance.
  • Treat every server action as a public endpoint and authorize the current user inside the action or middleware.
  • Keep farm.config.ts as the single control plane instead of spreading framework behavior across many root files.