EasyFM - v5.0.0-beta.8
    Preparing search index...

    Core Concepts

    This guide explains FileMaker ideas easy-fm builds on.

    Request flow usually looks like this:

    DatabaseProvider -> Database -> Layout -> Record

    And sometimes:

    LayoutRecord -> Portal -> PortalRecord

    DataApiProvider represents a FileMaker Data API connection. Custom providers can implement other transports.

    Example:

    const provider = new DataApiProvider({
    hostname: 'https://example.com',
    database: 'Contacts',
    credentials: { method: 'filemaker', username: 'api-user', password: 'secret' },
    externalSources: [],
    });

    Provider owns formatting rules and translates semantic database operations to its transport.

    Database represents one FileMaker file.

    Example:

    const database = Database.create({ provider });
    

    Database object handles session management, requests, layout caching, and helper methods like script().

    This part surprises many new users:

    FileMaker Data API works through layouts, not directly through tables.

    That means:

    • fields must be available on chosen layout if you want to read or write them
    • portal rows must be exposed on chosen layout if you want to fetch them
    • script execution can be layout-scoped

    Good practice:

    • create API-focused layouts
    • include fields needed by integration
    • avoid extra UI-only objects when possible

    LayoutRecord is one record returned from a layout.

    You read and write through record.fields:

    const record = await layout.records.get(123);
    record.fields.Status.value = 'Active';
    await record.commit();

    Important:

    • changing field value does not save immediately
    • commit() sends edits to FileMaker
    • get() re-fetches latest version from server

    Portals represent related rows returned inside parent layout record.

    Portal data is not fetched automatically. You must ask for it:

    const records = await layout.records
    .list({
    portals: {
    LineItems: {
    offset: 1,
    limit: 25,
    },
    },
    })
    .fetch();

    Then read portal rows from:

    records[0].portals.LineItems.records;
    

    FileMaker gives each record:

    • recordId: internal unique identifier
    • modId: internal modification counter

    These are useful for API operations.

    recordId should usually not be used as a business identifier because:

    • users do not recognize them
    • they are layout/API oriented
    • business rules usually belong to dedicated fields like CustomerNumber

    modId is different. It is still internal, but it can be useful for change detection, optimistic locking, or checking whether a record has been modified since you last read it.

    TypeScript users can describe layout shape to get better autocomplete and compile-time checks.

    Minimal example:

    import { type LayoutInterface, type Field } from '@jd-data-limited/easy-fm';

    interface ContactsLayout extends LayoutInterface {
    fields: {
    FirstName: Field<string>;
    LastName: Field<string>;
    };
    portals: {};
    }

    Then:

    const layout = database.layout<ContactsLayout>('Contacts_API');
    

    Now record.fields.FirstName is typed.