Agent Skills: .env

>

UncategorizedID: proofgeist/proofkit/typegen-fmodata

Install this agent skill to your local

pnpm dlx add-skill https://github.com/proofsh/proofkit/tree/HEAD/packages/fmodata/skills/typegen-fmodata

Skill Files

Browse the full folder contents for typegen-fmodata.

Download Skill

Loading file tree…

packages/fmodata/skills/typegen-fmodata/SKILL.md

Skill Metadata

Name
typegen-fmodata
Description
>

ALWAYS use @proofkit/typegen to set up an @proofkit/fmodata project. Typegen generates fmTableOccurrence definitions with correct field types and entity IDs (FMFID/FMTID) from FileMaker metadata. These entity IDs are opaque identifiers that cannot be guessed — they MUST come from typegen. Without typegen, queries using useEntityIds: true will silently fail or return wrong data. Before writing any fmodata query code, run typegen first.

Prerequisites

  • FileMaker Server 22.0.4+ accessible via port 443
  • OttoFMS 4.11+ installed on the FM server (only required for API key auth)
  • FileMaker account with the fmodata extended privilege enabled
  • OData API enabled on the FileMaker server

Setup

1. Install the runtime package

npm add @proofkit/fmodata

2. Set environment variables

# .env
FM_SERVER=https://your-server.com     # must start with https://
FM_DATABASE=MyFile.fmp12              # must end with .fmp12

# Option A: API key auth (requires OttoFMS 4.11+)
OTTO_API_KEY=dk_123456...789
# Option B: username/password auth
FM_USERNAME=admin
FM_PASSWORD=password

3. Initialize and run typegen

npx @proofkit/typegen@beta init

This creates proofkit-typegen-config.jsonc. Configure for OData mode:

{
  "$schema": "https://proofkit.dev/typegen-config-schema.json",
  "config": {
    "type": "fmodata",
    "path": "schema/odata",
    "tables": [
      {
        "tableName": "Customers",
        "fields": [
          { "fieldName": "InternalID", "exclude": true },
          { "fieldName": "Status", "typeOverride": "boolean" }
        ]
      },
      { "tableName": "Orders", "variableName": "OrdersTable" }
    ]
  }
}

Run typegen to generate schemas:

npx @proofkit/typegen@beta

Add a convenience script to package.json:

{
  "scripts": {
    "typegen": "npx @proofkit/typegen@beta"
    "typegen:ui": "npm run tyepgen ui"
  }
}

4. Create a server connection

import { FMServerConnection } from "@proofkit/fmodata";

export const connection = new FMServerConnection({
  serverUrl: process.env.FM_SERVER,
  auth: {
    apiKey: process.env.OTTO_API_KEY,
  },
});

5. First query

import { connection } from "./connection";
import { Customers } from "./schema/odata/generated/Customers";

const db = connection.database(process.env.FM_DATABASE);

const { data, error } = await db.from(Customers).list().execute();

if (error) {
  console.error(error);
} else {
  console.log(data);
}

Core Patterns

Generated output structure

For OData configs ("type": "fmodata"), typegen generates fmTableOccurrence definitions:

schema/odata/
  generated/      # Auto-generated. NEVER edit fields or entity IDs.
    Customers.ts  # fmTableOccurrence with fields, entity IDs from FM metadata
    Orders.ts

What typegen generates

Each generated file exports a fmTableOccurrence with:

  • Field definitions with correct types (textField(), numberField(), etc.)
  • Entity IDs (FMFID/FMTID) from FileMaker metadata
  • Primary key designation
  • Read-only markers for auto-enter fields
// schema/odata/generated/Customers.ts — generated by typegen, do NOT edit
import { fmTableOccurrence, textField, numberField, timestampField } from "@proofkit/fmodata";

export const Customers = fmTableOccurrence(
  "Customers",
  {
    id: textField().primaryKey().entityId("FMFID:1039485"),
    name: textField().notNull().entityId("FMFID:3432343"),
    email: textField().notNull().entityId("FMFID:1223242"),
    age: numberField().entityId("FMFID:4323435"),
    createdAt: timestampField().readOnly().entityId("FMFID:5938271"),
  },
  {
    entityId: "FMTID:1243253",
    defaultSelect: "schema",
  },
);

Safe customizations on generated schemas

You may add or edit these options on generated schemas — they will NOT be overwritten by typegen:

  • readValidator — transform data on read (e.g., z.coerce.boolean())
  • writeValidator — transform data on write
  • defaultSelect — control default field selection
  • navigationPaths — define relationship navigation
// Safe to add to generated file:
active: numberField()
  .readValidator(z.coerce.boolean())
  .writeValidator(z.boolean().transform((v) => (v ? 1 : 0)))
  .entityId("FMFID:6"),

Do NOT manually add new fields or change entity IDs — these must come from typegen.

Type inference

Use InferTableSchema to extract row types from generated tables:

import type { InferTableSchema } from "@proofkit/fmodata";
import { Customers } from "./schema/odata/generated/Customers";

type CustomerRow = InferTableSchema<typeof Customers>;

Custom env variable names

{
  "config": {
    "type": "fmodata",
    "envNames": {
      "server": "MY_FM_SERVER",
      "db": "MY_FM_DATABASE",
      "auth": {
        "apiKey": "MY_OTTO_KEY"
      }
    },
    "tables": [
      { "tableName": "Customers" }
    ]
  }
}

Multiple configs

The config key can be an array mixing fmdapi and fmodata entries, each with its own path and envNames.

Config options for OData tables

{
  "config": {
    "type": "fmodata",
    "path": "schema/odata",
    "reduceMetadata": true,
    "tables": [
      {
        "tableName": "Customers",
        "variableName": "CustomersTable",
        "fields": [
          { "fieldName": "InternalID", "exclude": true },
          { "fieldName": "Status", "typeOverride": "boolean" },
          { "fieldName": "Notes", "typeOverride": "string" }
        ]
      }
    ]
  }
}
  • variableName — custom export name (default: table name)
  • fields[].exclude — omit field from generated schema
  • fields[].typeOverride — override the inferred field type

Common Mistakes

CRITICAL: Inventing entity IDs or guessing field names

Wrong:

// Agent adds a field with a guessed entity ID
const contacts = fmTableOccurrence("contacts", {
  ...existingFields,
  newField: textField().entityId("FMFID:99"), // guessed ID — will silently fail
});

Entity IDs (FMFID/FMTID) are opaque and MUST come from FileMaker metadata via typegen. Guessed entity IDs cause silent query failures — wrong data or empty results with no error.

To add new fields, use the fmodata CLI to discover real field names programmatically, then add them to the tables[].fields array in proofkit-typegen-config.jsonc, and re-run typegen to generate the correct definitions with entity IDs:

# 1. Discover available fields via the CLI
npx @proofkit/fmodata@beta metadata fields --table <TableName>
# Add --details for field types, nullability, etc.

# 2. Edit proofkit-typegen-config.jsonc to include the new field(s)

# 3. Re-run typegen to regenerate schemas with correct entity IDs
npx @proofkit/typegen@beta

Never manually edit entity IDs or field definitions in generated files — always re-run typegen after config changes.

Source: packages/typegen/src/fmodata/typegen.ts

CRITICAL: Manually redefining types instead of using generated/inferred types

Wrong:

interface Customer {
  name: string;
  email: string;
  phone: string;
}

Correct:

import type { InferTableSchema } from "@proofkit/fmodata";
import { Customers } from "./schema/odata/generated/Customers";

type CustomerRow = InferTableSchema<typeof Customers>;

fmodata infers all types from fmTableOccurrence definitions. Use InferTableSchema<typeof table> if you need an explicit type alias.

Source: packages/typegen/src/buildSchema.ts

CRITICAL: Writing fmTableOccurrence from scratch without typegen

Wrong:

// Manually defining schema without entity IDs
const users = fmTableOccurrence("users", {
  id: textField().primaryKey(),
  name: textField().notNull(),
  email: textField().notNull(),
});

Correct:

npx @proofkit/typegen@beta

Manual schemas lack entity IDs, meaning useEntityIds: true (the recommended database option) won't work. Queries will use field names instead of stable IDs, breaking silently when fields are renamed in FileMaker. Always generate schemas with typegen.

Source: apps/docs/content/docs/fmodata/entity-ids.mdx

HIGH: Omitting type discriminator for OData config

Wrong:

{
  "config": {
    "tables": [
      { "tableName": "Customers" }
    ]
  }
}

Correct:

{
  "config": {
    "type": "fmodata",
    "tables": [
      { "tableName": "Customers" }
    ]
  }
}

Without "type": "fmodata", the config defaults to "fmdapi" and expects a layouts array instead of tables, causing a validation error.

Source: packages/typegen/src/types.ts:238-243

HIGH: Not running typegen after FileMaker schema changes or config edits

Wrong:

// Manually editing a generated file to add a new field
// OR editing proofkit-typegen-config.jsonc without re-running typegen

Correct:

npx @proofkit/typegen@beta

After changing table schemas in FileMaker or editing proofkit-typegen-config.jsonc, re-run typegen to regenerate types. The generated schemas are the source of truth for field names, types, and entity IDs.

Source: packages/typegen/src/fmodata/typegen.ts

CRITICAL: Putting env var values instead of names in config

Wrong:

{
  "envNames": {
    "server": "https://my-server.com",
    "auth": {
      "apiKey": "dk_abc123secret"
    }
  }
}

Correct:

{
  "envNames": {
    "server": "MY_FM_SERVER",
    "auth": {
      "apiKey": "MY_OTTO_KEY"
    }
  }
}

envNames expects the names of environment variables, not their secret values; typegen reads the actual values from the environment at runtime.

Source: apps/docs/content/docs/typegen/config.mdx

HIGH: Mixing Zod v3 and v4 in the same project

Use one Zod version consistently (v4 recommended). Zod v3 and v4 have incompatible APIs; mixing them causes runtime validation failures.

Source: apps/docs/content/docs/typegen/config.mdx

CRITICAL: FM_SERVER without https:// prefix

Wrong:

FM_SERVER=filemaker.example.com

Correct:

FM_SERVER=https://filemaker.example.com

@proofkit/fmodata expects FM_SERVER to be a full URL including the https:// protocol prefix.

Source: apps/docs/content/docs/fmdapi/quick-start.mdx

CRITICAL: Missing fmodata privilege on FM account

The OData API requires the fmodata extended privilege on the FileMaker account. Without it, all API calls return authorization errors. Enable via File > Manage > Security > Privilege Sets > Extended Privileges.

Source: apps/docs/content/docs/cli/guides/getting-started.mdx

References

  • fmodata-client: After generating schemas with typegen, see the fmodata-client skill for full query builder usage, CRUD operations, filter operators, relationships, batch operations, and error handling.
  • odata-query-optimization: Performance patterns for fmodata queries — defaultSelect, pagination, batch operations, and entity ID usage.