FD1 Client Protocol

fd1generic Javascript Library

fd1generic.js provides a client side Javascript module library that can simplify using fd1 protocol from javascript applications. You can connect directly from browsers to Fieldpine. Customer facing applications can also use this script, but take care not to embed secrets; users should use one off tokens, and you may like to use different domains

What fd1generic.js does:

  • Locate the host(s) for the backend fd1 api server(s)
  • Prompts user for login credentials or whatever is required. Consuming html pages can mostly ignore underlying security and CORS for the data API
  • Expose a FieldpineFd1 object for interacting with Fd1 servers.

<HTML><BODY>
<p>My demo page</p>


<script type="module">
    // fd1generic_loader.js is a tiny, long-cached shim. loadFd1() finds a working host,
    // loads the real fd1generic.js from it, and resolves to a ready FieldpineFd1 instance
    // (it calls Fieldpine_OpenServer() for you). Call it once and reuse the instance.
    import { loadFd1 } from "https://library.fieldpine.com/fd1generic_loader.js";

    const fp = await loadFd1();
    fp.Flags.push("allow-apikey");
    await fp.PromptLogin();                    // interactive; OR headless: fp.LoginPacket = { apikey: "..." }

    // Call the API - replace with desired commands
    const term = "milk";                       // whatever you are searching for
    const reply = await fp.SendMessage({
    a: "fd1.data",
    q:  { "description(like)": term },
    qo: { pid:true, description:true, "sku":"plucode", unitprice:true },
    v:  { table: "logical.products[publish=1]" }
    });
    // reply.data.rows[...]
</script>

</BODY></HTML>

Connection status, timeouts and abort (Fd1Loader)

loadFd1() retries forever (with backoff) until a host answers, so a page can sit through a network outage rather than failing. Import the Fd1Loader control object (from the same loader module) to tune the waits, hear connection progress, or give up. Values are read live, so you can set them before or after calling loadFd1() — a change applies on the next attempt. All of this is optional; the plain loadFd1() above needs none of it.

<script type="module">
    import { loadFd1, Fd1Loader } from "https://library.fieldpine.com/fd1generic_loader.js";

    // Tune waits (optional). Named constants; set only the ones you care about.
    Fd1Loader.hostTimeoutLastMs = 45000;      // be more patient with the only/last host

    // Hear progress (optional). phase = connecting | host-failed | sweep-failed | connected | aborted
    Fd1Loader.onStatus = s => {
        if (s.phase === "sweep-failed") showBanner("Can't reach server, retrying in " + Math.round(s.waitMs / 1000) + "s");
        if (s.phase === "connected")    hideBanner();
    };

    // Give up (optional). loadFd1() then rejects with an error whose .aborted === true.
    // cancelButton.onclick = () => { Fd1Loader.abort = true; };

    const fp = await loadFd1();
</script>

Methods

The examples below call methods on the fp instance returned by await loadFd1() (a FieldpineFd1 object).

DoLogin(Host, User, Pass, Apikey)

PromptLogin()

Displays a dialog asking the user for login details. Returns a promise that resolves after the user has entered their details. If the user clicks "ok", the method DoLogin() will automatically be called

ReceivedMessage(obj)

A function you can provide that is called for each message received. Ths receives the raw message object.

FieldpineFd1.ReceivedMessage = function(obj) {
    // ... your message handler here ...
}

SendMessage(obj)

Queues a message to send to the server. obj is a valid fd1 packet. Returns a promise that details results of the call.

If your obj packet does not have a "rq" value, one will be added automatically. Unless you have a specific need, let the library allocate "rq"

FieldpineFd1.SendMessage(
    {
        a: "fd1.products.get_products_list",
        qo: {
            physkey: true,
            description: true,
            sku: true
        }
    }
).then(function (reply) {
    // Reply is data.rows[ { physkey: ..., description: ..., sku: ...} , ...]
})

Response packets for FD1 are uniformly structured. The general form is

{
    r: ...
    rp: ...
    data: {
        rows: [ ... ],
        ...
    },
    error: [ ... ],
    warn: [ ... ],
    developer_notes: [ ... ]
}
the data{} component varies by the endpoint called. See protocol.htm for full details about the standard request/response structure.

fetch(uri, options)

Provides a fetch like access to the web socket allowing use of "fetch()" and promises rather than learning a new method, or for out of band requests that are just easier.

FieldpineFd1.fetch("/fd1/products/get_products_list",{
    body: JSON.stringify(
        { q: { pid: 123 } }
    )
})
.then(function (hreply) { return hreply.json() })
.then(function (packet) { 
    // ...packet.data has information
})

Notes

  • The URI must start /fd1/
  • The URI cannot contain any query parameters. It is blindly converted into the "a" field for Fd1
  • The body argument can be an object or a string, you do not need to JSON.stringify() as shown above
  • The function does not currently trigger any catch() handlers, but may in the future

Properties

LoginPacket

A object that is sent first when the websocket is opened. The method PromptLogin() can be used to create this object or you can manually create it. The object must confirm to the "v" portion of session.login

FieldpineFd1.LoginPacket = {
    apikey: "My secret key"
}

IdleTimeout

The amount of seconds to consider the link inactive, and close it down. The default is a couple of minutes, but may change. The timer is not checked very often, so the actual time before closing an inactive link can be higher than this value. If set to zero, the connection is not timed out from the client.

Regardless of this IdleTimeout value, the server may impose its own idle timers and reset links.

If you open firehoses, then you should increase this value, as a firehose message will not be delivered if the link is closed. Equally you probably want to close network connectons overnight.

A connection that is idle closed will reopen automatically when you attempt to send any packets.

Flags

An array that can contain optional keywords to alter the operation. To set a flag, simply push the string FieldpineFd1.Flags.push("no-connect")

Available flags are

no-connect Do not open the socket. If already open, this does not close it. This flag can be used to completely shutdown the socket and ensure that it does not accidentally open send requests to the server again

allow-apikey Instructs the PromptLogin() function to display an entry for apikey as well as other methods.

allow-testserver Instructs the PromptLogin() function to provide a quick option to connect to a test server. Typically only used during development

Description