FM Data API Client
NodeJS client for the FileMaker Data API, written in TypeScript. This library supports all FileMaker 17 features.
Requirements
- Node 0.12+
- node-fetch version 2.6 or higher
- Optionally @js-joda/core 3.0 or higher, when using the
DateUtils
helper.
Connecting to a server
In order to connect to a server, create a new instance of a client:
import {Client} from 'fm-data-api-client';
const client = new Client('https://file-maker-server', 'username', 'password', 'database');
Sign out
If you want to manually sign out to release the token, you can call the clearToken
method:
client.clearToken();
Retrieving a layout client
In order to work with layouts, you have to obtain the layout client. There are two ways to do this:
Untyped layout client
To get an untyped layout client, simply call:
const layout = client.layout('layout');
All operations on this layout client will result in untyped any
results, which means that you won't have any
type-safety or auto-completion within your IDE. If you prefer type-safety, have a look at the next section.
Typed layout client
To create a typed layout client, you first have to define the types of your field- and portal-data:
import {Numerish} from 'fm-data-api-client/Layout';
type MyFieldData = {
name : string;
createdAt : string;
quantity : Numerish;
};
type MyPortalData = {
portalA: {
name : string;
};
portalB: {
name : string;
};
};
As you can see, field data are just a simple object type, with the value type being either a string
or the special
Numerish
type (which internally is either a number
or a string
). The reason for the latter is that FileMaker will
return numbers as a string when they contain special characters, more on that later.
Now that you have defined your types, you can retrieve a typed layout client:
const layout = client.layout<MyFieldData, MyPortalData>('layout');
If you don't have any portal data, you can either ommit the second generic paramater or define it as an empty object.
Working with records
Now that you have created a layout client, you can use it to interact with the given layout.
Create a record
To create a record, all you have to do is to pass the (partial) field data to the create method:
const createResult = await layout.create({
name: 'foobar',
createdAt: '01/01/2020 15:00:00',
quantity: 5,
});
Update a record
Updating a record is just as easy as creating one:
const updateResult = await layout.update(recordId, {
name: 'baz',
});
Deleting a record
If you want to delete a record, this is even easier:
const deleteResult = await layout.delete(recordId);
Upload a file to a container
Once a record was created, you can upload files to a container field. You can either do this by supplying or path to a
file to upload or by passing in an object with two properties buffer
and name
:
await layout.upload(file, recordId, fieldName);
If the field is repetitive, you can also pass the repetition number als the fourth parameter (defaults to 1
).
Retrieve a single record
You can retrieve a single record directly via its record id:
const record = await layout.get(recordId);
Retrieve a range of records
If you only want to list the entire record set without searching, you can do so by querying for a range of records:
const records = await layout.range({
offset: 0,
limit: 100,
sort: {fieldName: 'name', sortOrder: 'ascend'},
});
All properties and the first parameter itself are optional.
Searching for records
If you need to search for specific records, you can do so via the find method:
const records = await layout.find({
query: {name: 'foobar'},
});
Additional parameters
All methods allow you to run scripts with each requests, either preRequests, preSort or after the processing. Please
refer to the FileMaker documentation for details. Unknown parameters will always be passed directly to the data API, so
you can also use any plain parameters defined by the API as well.
Value conversion
Due to FileMaker's nature, the data API will always return either strings or numbers, depending on the data type and
syntax. The three special cases to consider are numbers, booleans and any form of dates and times.
Numbers
As numbers can be returned as either number
or string
values, special care has to be taken when interpreting them.
If you have full control over the database, you could always assume them to be numbers. Otherwise you should play save
and use the number interpretation utility provided with this library:
import {parseNumber} from 'fm-data-api-client';
const realNumber = parseNumber(row.numerishValue);
This utility will return the input value when it already is a number
, otherwise it will apply the same parsing rules
as FileMaker does to the string. And empty value (after parsing) will result in null
being returned.
Booleans
As FileMaker has no data concept of booleans, these are represented as number fields. A value of 0
or an empty string
will be interpreted as false
, while any other value is true
:
import {parseBoolean} from 'fm-data-api-client';
const realBoolean = parseBoolean(row.boolishValue);
Dates and times
Last but not least, there are the special date and time values to consider. In order to parse these, you have to create
a new instance of DateUtil
first:
import {DateUtil} from 'fm-data-api-client';
const dateUtil = new DateUtil();
The DateUtil
constructor takes three optional arguments:
- dateFormat
- timeFormat
- dateTimeFormat
When not defining them, they will default to the US date and time format, which is the default for FileMaker. If the
format deviates, you have to define the correct format through these parameters. Please refer to this documentation for
the possible format patterns:
https://js-joda.github.io/js-joda/manual/formatting.html#format-patterns
Once you created a DateUtil
instance, you can use it to parse dates and times coming from FileMaker:
const localDate = dateUtil.parseDate(row.date);
const localTime = dateUtil.parseTime(row.time);
const localDateTime = dateUtil.parseTime(row.dateTime);
Since FileMaker does not provide a timezone with their date-times, the result will always be one of the following:
LocalTime
LocalDate
LocalDateTime
If you want to work with absolute date-time values, you have to interpret the given LocalDateTime
in the correct
timezone:
import {ZoneId} from '@js-joda/core';
const dateTime = localDateTime.atZone(ZoneId.of('America/Los_Angeles'));
For further information on how to work with those classes, please refer to the
js-joda documentation.