Skip to content
This page View Markdown Open in ChatGPT Open in Claude

Custom API examples

The code of a custom API : what to return, how to call the APIs of API Maker, how to send files.

Basic code

  • A custom API is an async function main(g) : what it returns is the data of the answer.
1
2
3
return {
    hello: 'world'
};

await

  • Every g.sys call returns a promise : await it. A call without await runs, but its result and its errors are lost.
1
2
3
return await g.sys.db.getAll({
    instance: "INSTANCE_NAME", database: 'DB_NAME', collection: "COLLECTION_NAME",
});

The second argument : true

  • Without it, a g.sys call returns the data and throws the errors of the API.
  • With true, it returns the whole envelope { success, statusCode, data, errors, warnings } and never throws : read success yourself.
1
2
3
4
5
6
7
8
9
let saveData = await g.sys.db.saveSingleOrMultiple({
    instance: "INSTANCE_NAME", database: 'DB_NAME', collection: "COLLECTION_NAME",
    saveData: {},
    headers: {}
}, true);
let countOfAllData = await g.sys.db.count({
    instance: "INSTANCE_NAME", database: 'DB_NAME', collection: "COLLECTION_NAME",
}, true);
return { saveData, countOfAllData };

Throw an error

  • throw ends the call with success: false and the message in errors. A message listed in the errorList of the API can be translated.
1
2
3
4
5
6
7
let saveData = await g.sys.db.saveSingleOrMultiple({
    instance: "INSTANCE_NAME", database: 'DB_NAME', collection: "COLLECTION_NAME",
    saveData: {},
    headers: {}
}, true);
if (saveData.success === false) throw 'Your data is not saved!';
else return saveData;

Return an image or any binary

  • Set g.res.contentType and return the base64 of the content.
g.res.contentType = 'image/gif';
return `data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOwA=`

Download file

import * as T from 'types';
import * as fs from 'fs';
import * as path from 'path';
async function main(g: T.IAMGlobal) {
    // always write file in uploads directlry which you want to send to user.
    // you can write other files in any directory which you don't want to send to user.
    let filePath = path.join(__dirname, 'uploads', 'myfile.txt');
    await fs.promises.writeFile(filePath, 'file 1 content', { encoding: 'utf8' });
    return {
        // just provide file name which is written to "uploads" directory
        __am__downloadFilePath: 'myfile.txt',
        __am__downloadFolderFileName: 'newName.txt', // download file will have this name.
    }
};
module.exports = main;
import * as T from 'types';
import * as fs from 'fs';
import * as path from 'path';
async function main(g: T.IAMGlobal) {
    return {
        __am__downloadFileOrFolderPaths: [
            {
                fsSource: path.join('folder1', 'folder2', 'folder3', 'data_folder'), // path into uploads folder
                archiveDestination: 'data_folder', // path in zip file, so data_folder folder will be directly available in zip file.
            },
        ],
        __am__downloadFolderFileName: `output.zip`, // download file will have this name.
    }
};
module.exports = main;

Download files & folders

import * as T from 'types';
import * as fs from 'fs';
import * as path from 'path';
async function main(g: T.IAMGlobal) {
    // always write file in uploads directlry which you want to send to user.
    // you can write other files in any directory which you don't want to send to user.
    let filePath = path.join(__dirname, 'uploads', 'myfile.txt');
    await fs.promises.writeFile(filePath, 'file 1 content', { encoding: 'utf8' });
    return {
        // You can provide list of files or folders, it will will make zip and send to user.
        __am__downloadFileOrFolderPaths: [
            'myfile.txt', // file name
            'someFolder', // folder name
        ],
        __am__downloadFolderFileName: 'zipFile.zip', // this will be zip file name
    }
};
module.exports = main;

Download file from Angular sent by custom API

async function downloadFileFromBrowser() {
    const requestPayload = {};
    const requestHeaders = {};
    const resp = <any>await this.http.post(`API Maker custom API endpoint`, requestPayload, {
        headers: requestHeaders,
        responseType: <any>'text',
        observe: 'response',
    }).toPromise();
    const fileName = resp.headers.get('content-filename');
    await this.downloadFileFromBlob('application/zip', resp.body, fileName);
    return resp;
}

async function downloadFileFromBlob(contentType, base64Data: any, fileName) {
    const a = document.createElement('a');
    document.body.appendChild(a);
    a.style.display = 'none';

    let blob;
    if (base64Data instanceof Blob) {
        blob = base64Data;
    } else {
        const base64Response = await fetch(`data:${contentType};base64,` + base64Data);
        blob = await base64Response.blob();
    }

    const url = window.URL.createObjectURL(blob);
    a.href = url;
    a.download = fileName;
    a.click();
    window.URL.revokeObjectURL(url);
}

Multi-tenant

  • Call a custom API with the x-am-tenant-username header to run it for one tenant of a multi-tenant instance.
  • It reads the tenant in g.req.params, and the APIs it calls with g.sys.db and g.sys.system work on the database of that tenant: the header is passed on, and each instance finds the tenant in its own tenants table.
  • instance: 'crm::globex' names another tenant for one call, and an empty tenant header names the structure database. See Custom APIs of a multi-tenant instance.
GET /api/custom-api/admin/customers-report
x-am-tenant-username: acme
1
2
3
4
5
6
7
8
9
import * as T from 'types';

async function main(g: T.IAMGlobal) {
    const tenant = g.req.params.tenantUsername; // 'acme'
    const customers = await g.sys.db.getAll({ instance: 'crm', database: 'crm', collection: 'customers' }); // from the database of acme
    const customersOfGlobex = await g.sys.db.count({ instance: 'crm::globex', database: 'crm', collection: 'customers' });
    return { tenant, customers, customersOfGlobex };
}
module.exports = main;