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 :
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 :
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
constperson=g.req.auth.authAMDB;// the row of your users table, minus its passwordconstapiUser=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 passwordapi_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
varperson=g.Req.Auth.GetObject("authAMDB");// the row of your users table, minus its passwordvarapiUser=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 passwordapiUser:=g.Req.Auth().Map("authAMUser")// name and groups of the API user
Who is calling
letperson=&g.req.auth()["authAMDB"];// the row of your users table, minus its passwordletapi_user=&g.req.auth()["authAMUser"];// name and groups of the API user
Who is calling
JsonObjectperson=g.req.getAuth().getObject("authAMDB");// the row of your users table, minus its passwordJsonObjectapiUser=g.req.getAuth().getObject("authAMUser");// name and groups of the API user
Who is calling
valperson=g.req.auth.getObject("authAMDB")// the row of your users table, minus its passwordvalapiUser=g.req.auth.getObject("authAMUser")// name and groups of the API user
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.
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.
constorder={_id:'A-1001'};g.logger.log('order ',order._id);// with the response on the testing pages, in the logs of a log profileg.logger.error('payment failed : ','card declined');g.res.shared.count=234;// set in a pre hook…constcount=g.res.shared.count;// …read in the post hook of the same requestg.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 profileg.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 requestg.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
varorder=new{_id="A-1001"};g.Logger.Log("order",order._id);// with the response on the testing pages, in the logs of a log profileg.Logger.Error("payment failed :","card declined");g.Res.Shared["count"]=234;// set in a pre hook…varcount=g.Res.Shared.GetInt64("count");// …read in the post hook of the same requestg.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 profileg.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 requestg.Shared.Update("visits",func(oldany)any{// kept by the runner for the requests which followvisits,_:=old.(int)returnvisits+1})
letorder=json!({"_id":"A-1001"});g.logger.log(format!("order {}",order["_id"]));// with the response on the testing pages, in the logs of a log profileg.logger.error("payment failed : card declined");g.res.shared_mut()["count"]=json!(234);// set in a pre hook…letcount=g.res.shared()["count"].as_i64();// …read in the post hook of the same requestg.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
JsonObjectorder=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 profileg.logger.error("payment failed :","card declined");g.res.getShared().put("count",234);// set in a pre hook…longcount=g.res.getShared().getLong("count");// …read in the post hook of the same requestg.shared.merge("visits",1,(old,one)->(Integer)old+1);// kept by the runner for the requests which follow
valorder=mapOf("_id"to"A-1001")g.logger.log("order",order["_id"])// with the response on the testing pages, in the logs of a log profileg.logger.error("payment failed :","card declined")g.res.shared["count"]=234// set in a pre hook…valcount=g.res.shared.getLong("count")// …read in the post hook of the same requestg.shared.merge("visits",1){old,_->(oldasInt)+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.