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

The global object g

Every piece of code you write in API Maker, a custom API, a hook, an event listener, a scheduler, a migration, a process initializer, a test case, is a function of one argument, g. In TypeScript :

1
2
3
4
5
6
7
8
9
import * as T from 'types';
import * as db from 'db-interfaces';

async function main(g: T.IAMGlobal) {
    const rows = await g.sys.db.getAll<db.mongodb.shop.IProducts>({ instance: 'mongodb', database: 'shop', collection: 'products' });
    g.logger.log(rows.length, 'products');
    return rows;
}
module.exports = main;
1
2
3
4
5
6
7
from am import G


async def main(g: G):
    rows = await g.sys.db.get_all('mongodb', 'shop', 'products')
    g.logger.log(len(rows), 'products')
    return rows
1
2
3
4
5
6
7
<?php
function main(AmG $g)
{
    $rows = $g->sys->db->getAll('mongodb', 'shop', 'products');
    $g->logger->log(count($rows), 'products');
    return $rows;
}
1
2
3
4
5
6
7
8
9
file static class Api
{
    public static async Task<object?> Main(G g)
    {
        var rows = await g.Sys.Db.GetAll(new GetAll("mongodb", "shop", "products"));
        g.Logger.Log(rows.Count, "products");
        return rows;
    }
}
package code

import "apimaker/am"

func Main(g *am.G) (any, error) {
    rows, err := g.Sys.Db.GetAll(am.GetAll{Instance: "mongodb", Database: "shop", Collection: "products"})
    if err != nil {
        return nil, err
    }
    g.Logger.Log(len(rows), "products")
    return rows, nil
}
1
2
3
4
5
6
7
use am::{Errors, G, GetAll, Value, json};

pub fn main(g: &mut G) -> Result<Value, Errors> {
    let rows = g.sys.db.get_all(GetAll::new("mongodb", "shop", "products"))?;
    g.logger.log(format!("{} products", rows.len()));
    Ok(json!(rows))
}
import am.G;
import am.GetAll;
import am.JsonObject;
import java.util.List;

class Api {
    static Object main(G g) {
        List<JsonObject> rows = g.sys.db.getAll(new GetAll("mongodb", "shop", "products"));
        g.logger.log(rows.size(), "products");
        return rows;
    }
}
1
2
3
4
5
6
7
8
import am.G
import am.GetAll

fun main(g: G): Any? {
    val rows = g.sys.db.getAll(GetAll("mongodb", "shop", "products"))
    g.logger.log(rows.size, "products")
    return rows
}
g.req What came in : headers, params, query, body, event data, the opened tokens.
g.res What goes out : status code, content type, output, headers, errors, warnings.
g.sys The APIs of API Maker from code : db, db.gen, system, cache, and test inside a test case.
g.logger debug, log, info, warn, error : shown with the response on the testing pages, kept by the log profiles.
g.shared Values every request of the same runner shares, for as long as it runs : a cache, a counter.

In every language

Custom APIs, hooks, schedulers and event listeners can also be written in Python, PHP, C#, Go, Rust, Java and Kotlin. They get the same g, in the words of their language :

Request Answer Database Logger Shared Refuse
TypeScript g.req.body.id g.res.statusCode = 201 await g.sys.db.getAll({…}) g.logger.log('a') g.shared.n throw [{ code: 400, message }]
Python g.req.body.id g.res.status_code = 201 await g.sys.db.get_all('mysql_8', 'inventory', 'customers', limit=10) g.logger.log('a') g.shared.get('n') raise g.Errors(message, code=400)
PHP $g->req->body->id $g->res->statusCode = 201 $g->sys->db->getAll('mysql_8', 'inventory', 'customers', limit: 10) $g->logger->log('a') $g->shared->n throw new AmErrors($message, 400)
C# g.Req.Body["id"] g.Res.StatusCode = 201 await g.Sys.Db.GetAll(new GetAll("mysql_8", "inventory", "customers") { Limit = 10 }) g.Logger.Log("a") g.Shared.GetValueOrDefault("n") throw new AmErrors(message, 400)
Go g.Req.Body().Get("id") g.Res.SetStatusCode(201) g.Sys.Db.GetAll(am.GetAll{Instance: "mysql_8", Database: "inventory", Collection: "customers", Limit: 10}) g.Logger.Log("a") g.Shared.Get("n") return nil, am.Fail(400, message)
Rust g.req.body()["id"] g.res.set_status_code(201) g.sys.db.get_all(GetAll::new("mysql_8", "inventory", "customers").limit(10))? g.logger.log("a") g.shared.get("n") Err(am::fail(400, message))
Java g.req.getBody().get("id") g.res.setStatusCode(201) g.sys.db.getAll(new GetAll("mysql_8", "inventory", "customers").limit(10)) g.logger.log("a") g.shared.get("n") throw new AmErrors(message, 400)
Kotlin g.req.body["id"] g.res.statusCode = 201 g.sys.db.getAll(GetAll("mysql_8", "inventory", "customers").limit(10)) g.logger.log("a") g.shared["n"] throw AmErrors(message, 400)

Each language page has the full reference and examples : TypeScript · Python · PHP · C# · Go · Rust · Java · Kotlin.

The code editor completes g in each of them, and offers the columns of your tables on the rows a call answers : the code editor in every language.

g.req

Key Holds
headers The request headers, editable : a pre hook can set x-am-content-type-response for the whole call.
params The path parameters : instanceName, database, collection, id, primaryKey, field and order of distinct, tenantUsername for a multi-tenant request…
query The query parameters as an object : find, select, limit… editable in a pre hook, which is how row level security is done.
body The body, editable.
eventData The data of the event, in an event listener.
auth The opened tokens : authAMUser (the API user), authAMDB (the person, from a DB auth provider), authCustom, authGoogle, authAWS, authAzure.
reqInfo url, apiCategory, reqMethod and apiInfo (id, name, schemaType) : which API is running.
isApiRequestFromUser true for an HTTP request, false for a call made from code.
Who is calling
const person = g.req.auth.authAMDB;      // the row of your users table, minus its password
const apiUser = g.req.auth.authAMUser;   // name and groups of the API user
Who is calling
person = g.req.auth.authAMDB       # the row of your users table, minus its password
api_user = g.req.auth.authAMUser   # name and groups of the API user
Who is calling
$person = $g->req->auth->authAMDB;       // the row of your users table, minus its password
$apiUser = $g->req->auth->authAMUser;    // name and groups of the API user
Who is calling
var person = g.Req.Auth.GetObject("authAMDB");       // the row of your users table, minus its password
var apiUser = g.Req.Auth.GetObject("authAMUser");    // name and groups of the API user
Who is calling
person := g.Req.Auth().Map("authAMDB")    // the row of your users table, minus its password
apiUser := g.Req.Auth().Map("authAMUser") // name and groups of the API user
Who is calling
let person = &g.req.auth()["authAMDB"];       // the row of your users table, minus its password
let api_user = &g.req.auth()["authAMUser"];   // name and groups of the API user
Who is calling
JsonObject person = g.req.getAuth().getObject("authAMDB");       // the row of your users table, minus its password
JsonObject apiUser = g.req.getAuth().getObject("authAMUser");    // name and groups of the API user
Who is calling
val person = g.req.auth.getObject("authAMDB")       // the row of your users table, minus its password
val apiUser = g.req.auth.getObject("authAMUser")    // name and groups of the API user

g.res

Key Holds
statusCode T.EStatusCode.OK (200), CREATED, NO_CONTENT, BAD_REQUEST, UNAUTHORIZED, FORBIDDEN, RESOURCE_NOT_FOUND, INTERNAL_SERVER_ERROR… Set by your code, it wins over the one of the API.
contentType T.EContentType.JSON (default), XML, YAML, TEXT, HTML, OCTET_STREAM.
output The answer. In a post hook, the answer of the API, editable. A custom API returns its output instead.
headers Headers to add to the reply.
errors, warnings The lists of the reply.
shared Values of this request : set in a pre hook, read in the API or its post hook.
An HTML page from a custom API
g.res.contentType = T.EContentType.HTML;
return '<h1>Hello</h1>';
An HTML page from a custom API
g.res.content_type = 'text/html'
return '<h1>Hello</h1>'
An HTML page from a custom API
$g->res->contentType = 'text/html';
return '<h1>Hello</h1>';
An HTML page from a custom API
g.Res.ContentType = "text/html";
return "<h1>Hello</h1>";
An HTML page from a custom API
g.Res.SetContentType("text/html")
return "<h1>Hello</h1>", nil
An HTML page from a custom API
g.res.set_content_type("text/html");
Ok(json!("<h1>Hello</h1>"))
An HTML page from a custom API
g.res.setContentType("text/html");
return "<h1>Hello</h1>";
An HTML page from a custom API
g.res.contentType = "text/html"
return "<h1>Hello</h1>"
  • To send a file, return an object with __am__downloadFilePath or __am__downloadFileOrFolderPaths : see files.

g.sys

Part Holds
g.sys.db The schema APIs of any table : getAll, getById, query, saveSingleOrMultiple, masterSave, updateById, updateMany, replaceById, removeById, removeByQuery, aggregate, count, distinct, distinctQuery, arrayOperations, getAllByStream, queryByStream. Examples.
g.sys.db.gen The same calls on the generated APIs, without schema : getAllGen, queryGen… Examples.
g.sys.system The system APIs : encrypt, decrypt, hash, getToken, getSecret, callExternalApi, executeQuery, getTableMeta, createIndexes, dropIndexes, getIndexes, emitEvent, emitEventWS, isValidDataForTable, isValidDataForCustomAPI, multiTenantInstanceUpdated, isValidConnectionString.
g.sys.cache Your own keys in Redis and the cache resets : getKey, setKey, removeKey, resetCacheDB, resetCacheCustomApis, resetCacheSystemApis. Examples.
g.sys.test In a test case only : runCustomApi, mock, clearMocks, calls.
  • Every g.sys.db call takes the same headers and queryParams an HTTP call takes, and runs with the groups of the API user of the request. Hooks are skipped unless you pass skipHookRunning: false.
  • Pass true as the last argument (getFullResponse) in TypeScript, or call through full in the other languages (g.sys.db.full.get_all in Python, g.Sys.Db.Full.GetAll in C# and Go), to get the whole { success, data, errors, … } envelope instead of data.

g.logger and g.shared

const order = { _id: 'A-1001' };
g.logger.log('order ', order._id);         // with the response on the testing pages, in the logs of a log profile
g.logger.error('payment failed : ', 'card declined');
g.res.shared.count = 234;                  // set in a pre hook…
const count = g.res.shared.count;          // …read in the post hook of the same request
g.shared.visits = (g.shared.visits || 0) + 1;   // kept by the runner for the requests which follow
order = {'_id': 'A-1001'}
g.logger.log('order', order['_id'])        # with the response on the testing pages, in the logs of a log profile
g.logger.error('payment failed :', 'card declined')
g.res.shared.count = 234                   # set in a pre hook…
count = g.res.shared.count                 # …read in the post hook of the same request
g.shared['visits'] = g.shared.get('visits', 0) + 1   # kept by the runner for the requests which follow
$order = ['_id' => 'A-1001'];
$g->logger->log('order', $order['_id']);   // with the response on the testing pages, in the logs of a log profile
$g->logger->error('payment failed :', 'card declined');
$g->res->shared->count = 234;              // set in a pre hook…
$count = $g->res->shared->count;           // …read in the post hook of the same request
$g->shared->visits = ($g->shared->visits ?? 0) + 1;   // kept by the runner for the requests which follow
var order = new { _id = "A-1001" };
g.Logger.Log("order", order._id);          // with the response on the testing pages, in the logs of a log profile
g.Logger.Error("payment failed :", "card declined");
g.Res.Shared["count"] = 234;               // set in a pre hook…
var count = g.Res.Shared.GetInt64("count"); // …read in the post hook of the same request
g.Shared.AddOrUpdate("visits", 1, (_, old) => (int)old! + 1);   // kept by the runner for the requests which follow
order := am.M{"_id": "A-1001"}
g.Logger.Log("order", order["_id"]) // with the response on the testing pages, in the logs of a log profile
g.Logger.Error("payment failed :", "card declined")
g.Res.Shared()["count"] = 234                // set in a pre hook…
count := g.Res.Shared().Int("count")         // …read in the post hook of the same request
g.Shared.Update("visits", func(old any) any { // kept by the runner for the requests which follow
    visits, _ := old.(int)
    return visits + 1
})
let order = json!({ "_id": "A-1001" });
g.logger.log(format!("order {}", order["_id"]));   // with the response on the testing pages, in the logs of a log profile
g.logger.error("payment failed : card declined");
g.res.shared_mut()["count"] = json!(234);           // set in a pre hook…
let count = g.res.shared()["count"].as_i64();       // …read in the post hook of the same request
g.shared.update("visits", |old| json!(old.and_then(|n| n.as_i64()).unwrap_or(0) + 1));   // kept by the runner for the requests which follow
JsonObject order = JsonObject.of("_id", "A-1001");
g.logger.log("order", order.get("_id"));          // with the response on the testing pages, in the logs of a log profile
g.logger.error("payment failed :", "card declined");
g.res.getShared().put("count", 234);               // set in a pre hook…
long count = g.res.getShared().getLong("count");   // …read in the post hook of the same request
g.shared.merge("visits", 1, (old, one) -> (Integer) old + 1);   // kept by the runner for the requests which follow
val order = mapOf("_id" to "A-1001")
g.logger.log("order", order["_id"])           // with the response on the testing pages, in the logs of a log profile
g.logger.error("payment failed :", "card declined")
g.res.shared["count"] = 234                    // set in a pre hook…
val count = g.res.shared.getLong("count")      // …read in the post hook of the same request
g.shared.merge("visits", 1) { old, _ -> (old as Int) + 1 }   // kept by the runner for the requests which follow
  • debug, log, info, warn and error write the same line : the level is not kept. Objects are printed as indented JSON. In TypeScript the arguments are joined without a separator, in the other languages with a space. The calls are synchronous, no await.
  • For an error, log its message (e.message in TypeScript) or its stack.
  • On the native process (runOnNativeProcess), console.log is not captured : use g.logger.
  • g.shared belongs to one runner : a sandbox has several runners, and a server several sandboxes, so a value there is a cache, never the only copy of something. Values for the rest of one request go in g.res.shared.