Farm.js

Farm Infra analytics

Farm reports request and MCP tool activity to your Farm Infra project when the server's environment links the app to one. There's no package to install and nothing to add to farm.config.ts.

.env
FARMJS_API_KEY=farmjs_…
FARMJS_PROJECT_ID=proj_…

Copy the project ID from the project's page in Farm Infra. Set the variables wherever the server runs: .env for farm dev, and your host's environment variables for production. Farm reads them when the server starts, so the build doesn't need them.

An app without these variables sends nothing. FARMJS_API_KEY on its own isn't enough, because the same key also authorizes other Farm Infra APIs.

Write-only keys

An account API key belongs to a person. It stops sending if they revoke it or leave the organization, and on a server it can also start previews and run site checks as them. For a key that can only write analytics, create a write-only key for the project and environment in Farm Infra and set that instead:

.env
FARMJS_API_KEY=fti_…

A write-only key names its own project and environment, so FARMJS_PROJECT_ID isn't needed.

Environment variables

VariablePurpose
FARMJS_API_KEYAn account API key (farmjs_…) or a project's write-only key (fti_…).
FARMJS_PROJECT_IDThe Farm Infra project to report to. Needed with an account API key.
FARMJS_ENVIRONMENTproduction, staging or development. Defaults to Vercel's environment (preview is staging), then NODE_ENV.
FARMJS_API_URLFarm Infra's address, if it isn't https://infra.farmjs.dev. Must be HTTPS, except for localhost.

A value Farm can't use, such as a malformed key or project ID, leaves analytics off and prints one warning when the server starts. It never stops the app. If Farm Infra later rejects the key, Farm stops sending until the server restarts and prints one warning.

Turn it off

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

export default defineConfig({
  analytics: false,
});

With analytics: false, the production server doesn't include the analytics code at all. Removing the environment variables also turns it off.

What is sent

  • For each request: a random ID, the matched route template (such as /users/[id]), the method, the response status and the time until Farm returned the response.
  • For each registered MCP tool call: the tool name, whether it succeeded, and its duration, linked to its request.
  • A self-reported agent category when the user-agent declares one of OpenAI's or Anthropic's crawlers or agents (GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, Claude-SearchBot, Claude-User). This is the user-agent's claim, not a verified identity. Anything else is unknown.

Farm never sends URLs, query strings, route parameters, headers, cookies, IP addresses, full user-agents, request or response bodies, tool arguments or results, or error messages. Requests that match no route are reported as /[unmatched].

Delivery

Analytics never delays or changes a response. Events are queued in memory and sent in the background through the runtime's waitUntil, in batches of up to 50, with a two-second timeout per request and bounded retries. At most 500 events are held, and an event is dropped after five minutes or five failed attempts. When the server shuts down, Farm tries once more to send what's queued. It's best-effort: a crash or a platform's runtime limit can drop events.

What it can't see

Only requests that reach Farm's server runtime are counted. Pages served as static files, by a CDN cache or blocked by a firewall before reaching the app, aren't. Turning analytics on doesn't make a static page dynamic. Unhandled exceptions with no final response aren't given an invented status.

It runs on Node-compatible server runtimes. Edge runtimes are not supported yet.

This is separate from Farm's own product telemetry, which is controlled by the telemetry option, and from OpenTelemetry.