WebViewerAdapter
WebViewerAdapter is the piece of @adt/fmdapi that makes the DataApi client work inside a FileMaker Web Viewer. There is no server-side Data API to enable and no token to manage. Record operations and container uploads run through FileMaker scripts.
Note Generated clients already do this If you're using the client generated by adt typegen, you won't construct a WebViewerAdapter yourself — it's wired up for you, pointed at ADT_execute_data_api. This page is for understanding what's happening under the hood, or for the rare case where you need a custom client. See Overview and Use a typed data client for the generated-client walkthrough.
The request path
A normal Data API adapter sends an HTTP request to a FileMaker Server. WebViewerAdapter instead calls a FileMaker script — by default ADT_execute_data_api, installed into your file by the Agentic Development Toolkit (ADT) with adt components apply — using the Web Viewer's PerformScript bridge:
-
Your code calls a
DataApimethod (list,find,get,create,update,delete,layoutMetadata). -
WebViewerAdapterturns that call into a Data API–shaped payload (action,layouts,version: "vLatest", plus the request body). -
It calls ADT_execute_data_api through the Web Viewer bridge, passing the payload as the script parameter.
-
The script runs the FileMaker Data API engine locally against the file and returns the raw Data API response as the script result.
-
The adapter parses that response and resolves your original call — or throws a
FileMakerErrorif the Data API reported an error.
Because the script runs inside the file, requests use the current user's FileMaker session and permissions. There's no separate authentication step and no Data API needs to be turned on for FileMaker Server.
Constructing an adapter
export const client = DataApi({
adapter: new WebViewerAdapter({
scriptName: "ADT_execute_data_api",
}),
layout: "Contacts",
});
WebViewerAdapter takes:
| Option | Type | Description |
|---|---|---|
scriptName
|
string
|
The FileMaker script to call for every request. Use ADT_execute_data_api, the script adt components apply installs, unless you have a reason to point at a different script. |
batch
|
boolean | { enabled?: boolean; windowMs?: number; maxSize?: number }
|
Controls request coalescing. See Batching below. |
pageSize
|
number
|
Page size used internally by listAll / findAll when a request doesn't specify its own limit. Defaults to 100. |
container
|
{ scriptName?: string; timeoutMs?: number; maxFileBytes?: number }
|
Configures container uploads. See Container uploads below. |
WebViewerAdapter can also be imported from the @adt/fmdapi/adapter subpath to avoid pulling in the rest of the package.
DataApi returns the same client shape regardless of adapter. list, listAll, get, find, findAll, findFirst, maybeFindFirst, create, update, delete, layoutMetadata, and containerUpload work with WebViewerAdapter.
executeScript() is the one unsupported Adapter method. Call FileMaker scripts directly with fmFetch or callFMScript instead. See Script Calls.
Container uploads
containerUpload converts the supplied file to base64 and calls ADT_container_upload, a separate FileMaker script from the one used for record operations.
const adapter = new WebViewerAdapter({
scriptName: "ADT_execute_data_api",
container: {
scriptName: "ADT_container_upload",
timeoutMs: 60_000,
maxFileBytes: 20 * 1024 * 1024,
},
});
The container settings are optional:
| Option | Default | Description |
|---|---|---|
scriptName
|
"ADT_container_upload"
|
FileMaker script that receives and writes the base64 file data. |
timeoutMs
|
60000
|
How long the client waits for the script callback. Set it to 0 or a negative number to wait without a timeout. |
maxFileBytes
|
20971520
|
Maximum source-file size in bytes. Set it to 0 or a negative number to disable this client-side limit. |
The upload requires a file name with an extension and currently supports only the first repetition of a repeating container field. The method throws FileMakerError when FileMaker reports an error.
If the callback does not arrive before the timeout, it throws ContainerUploadTimeoutError. This error includes the scriptName, timeoutMs, and outcome: "unknown". The outcome is unknown because FileMaker may finish writing the file after the client stops waiting.
See containerUpload for the method arguments and a complete call.
Batching
WebViewerAdapter coalesces Data API calls that happen within a short window into a single script call, by default. Instead of one PerformScript round trip per request, multiple queued requests are sent as one call with a top-level requests array:
{
"requests": [
{ "id": "batch-0", "action": "read", "layouts": "Contacts", "version": "vLatest" },
{ "id": "batch-1", "action": "read", "layouts": "Orders", "version": "vLatest" }
]
}
ADT_execute_data_api runs each entry against the Data API engine in order and returns a matching responses array, keyed by id. FileMaker still executes the underlying Data API operations sequentially — batching reduces Web Viewer bridge round trips, not FileMaker-side work.
The default batch window is 8ms with a maximum batch size of 20 requests:
new WebViewerAdapter({
scriptName: "ADT_execute_data_api",
batch: {
windowMs: 16,
maxSize: 10,
},
});
Set batch: false on the adapter to send every request on its own, one call at a time:
new WebViewerAdapter({
scriptName: "ADT_execute_data_api",
batch: false,
});
Per-request control
Override the adapter's setting for an individual call:
// keep this call out of any batch
await client.list({ batch: false, limit: 10 });
// coalesce this call even if the adapter has batching disabled
await client.find({
batch: true,
query: { status: "Active" },
});
When a request sets batch: true on an adapter configured with batch: false, it uses the library defaults (8ms window, batch size 20).
Warning Benchmark with your own layouts FileMaker performance depends on whether the script runs locally or on FileMaker Server, plus the layout, table, found set, and any portal or container fields involved. Treat windowMs and maxSize as a starting point, not a universal setting — measure with your own layouts before tuning them.
listAll and findAll
listAll() and findAll() don't send an unbounded readAll request. Instead, the adapter sends one bounded read request first, reads dataInfo.foundCount from the response, and calculates how many more bounded pages it needs. Follow-up pages are fetched next — batched in groups up to maxSize when batching is enabled — until every record has been retrieved.
This keeps the FileMaker script from being asked to load an unknown number of records at once, but it can still mean many requests against a large found set. Prefer find() with an explicit limit/offset for anything user-facing, and reserve listAll() / findAll() for cases where you deliberately need the full found set.
Errors
When the FileMaker Data API returns a non-zero error code, WebViewerAdapter throws a FileMakerError:
try {
await client.find({ query: { Status: "Archived" } });
} catch (error) {
if (error instanceof FileMakerError) {
console.error(error.code, error.message);
}
}
FileMakerError carries the Data API error code as a string (for example "401" for "no records match the request") alongside a descriptive message. Pass ignoreEmptyResult: true on a find() call — or use findFirst / maybeFindFirst — where an empty result is an expected outcome rather than an error.