Skip to main content

Node.js

For Node.js 18 or later, using node:crypto and the built in fetch. No packages need to be installed.

The API must be enabled first, see Enabling the API. How a request is signed is explained in Authentication.

Client​

const crypto = require('node:crypto');

const SERVER_URL = 'http://localhost:19200';
const API_TOKEN = 'YOUR_API_TOKEN';

// calls the FileFlows API, each request is signed with the API token (the token is never sent)
async function fileflowsApi(method, path, body = null) {
const json = body === null ? '' : JSON.stringify(body);
const timestamp = Math.floor(Date.now() / 1000).toString();
const nonce = crypto.randomUUID().replace(/-/g, '');
const bodyHash = json === '' ? '' : crypto.createHash('sha256').update(json, 'utf8').digest('hex');
const signedPath = decodeURIComponent(path.split('?')[0]);
const stringToSign = method.toUpperCase() + ':' + signedPath + ':' + timestamp + ':' + nonce + ':' + bodyHash;
const signature = crypto.createHmac('sha256', API_TOKEN).update(stringToSign, 'utf8').digest('hex');

const headers = { 'X-Timestamp': timestamp, 'X-Nonce': nonce, 'X-Signature': signature };
if (json !== '')
headers['Content-Type'] = 'application/json';
const response = await fetch(SERVER_URL + path, { method, headers, body: json === '' ? undefined : json });
const text = await response.text();
if (!response.ok)
throw new Error(response.status + ': ' + text);
return text ? JSON.parse(text) : null;
}

// const status = await fileflowsApi('GET', '/api/status');

Replace http://localhost:19200 with the address of your FileFlows server, and YOUR_API_TOKEN with the token from Config > API. Load the token from an environment variable, e.g. process.env.FILEFLOWS_API_TOKEN, rather than hard coding it.

Usage​

(async () => {
const status = await fileflowsApi('GET', '/api/status');
console.log(status);
})();

To send a JSON body, pass it as the body argument. The function serializes it once, and signs and sends that same JSON text.

TypeScript​

The same function in TypeScript:

import { createHash, createHmac, randomUUID } from 'node:crypto';

const SERVER_URL = 'http://localhost:19200';
const API_TOKEN = 'YOUR_API_TOKEN';

type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';

// calls the FileFlows API, each request is signed with the API token (the token is never sent)
export async function fileflowsApi<T>(method: HttpMethod, path: string, body: unknown = null): Promise<T | null> {
const json: string = body === null ? '' : JSON.stringify(body);
const timestamp: string = Math.floor(Date.now() / 1000).toString();
const nonce: string = randomUUID().replace(/-/g, '');
const bodyHash: string = json === '' ? '' : createHash('sha256').update(json, 'utf8').digest('hex');
const signedPath: string = decodeURIComponent(path.split('?')[0]);
const stringToSign: string = [method, signedPath, timestamp, nonce, bodyHash].join(':');
const signature: string = createHmac('sha256', API_TOKEN).update(stringToSign, 'utf8').digest('hex');

const headers: Record<string, string> = { 'X-Timestamp': timestamp, 'X-Nonce': nonce, 'X-Signature': signature };
if (json !== '')
headers['Content-Type'] = 'application/json';
const response = await fetch(SERVER_URL + path, { method, headers, body: json === '' ? undefined : json });
const text: string = await response.text();
if (!response.ok)
throw new Error(`${response.status}: ${text}`);
return text ? (JSON.parse(text) as T) : null;
}

// const status = await fileflowsApi<{ queue: number }>('GET', '/api/status');