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

Response format

Every API of API Maker, generated, schema, custom, system or third party, answers with the same envelope, so one client handles all of them the same way.

The envelope

A successful answer
{
    "success": true,
    "statusCode": 200,
    "data": [ { "customer_id": 1, "first_name": "Bob", "last_name": "Lin" } ],
    "totalCount": 5,
    "warnings": [],
    "logs": [],
    "meta": {}
}
  • data holds the result, totalCount comes with getTotalCount=true, warnings and logs when there are some, meta with the header x-am-meta: true. Absent keys are simply not sent.
A refused request
{
    "success": false,
    "statusCode": 400,
    "errors": [
        { "type": "required", "field": "first_name", "message": "Please provide valid 'first_name' field with type 'string'.", "code": 400 }
    ],
    "warnings": []
}
  • errors lists every problem found, each with its code, message, and for data problems the field and the type of the rule. The HTTP status of the reply is statusCode.
Key When Meaning
success always true when the request did what it was asked, false otherwise.
statusCode always The HTTP status of the reply, repeated in the body : 200, 201 for a save, 400, 401, 403, 404 or 500.
data on success, and on some failures The result : an array for get all and the query APIs, an object for get by id, a save of one object, an update ; whatever a custom API returns. null when there is nothing.
totalCount with getTotalCount=true The number of rows matching the filter, without skip and limit.
errors on failure One entry per problem. Data problems name the field and the type of the rule ; the message follows the language asked with x-am-internationalization.
warnings when there are some Problems which did not stop the request, in the same shape as errors : for example a deep which found no target.
logs when your code logged What g.logger printed while a custom API, a hook or an event ran, so the caller can see it.
meta with x-am-meta: true executionTime, executionPlan (MongoDB explain), apiAccessGroups (which group granted the call) and runBy (which server, process and sandbox ran the code).
encryptedData with x-am-get-encrypted-data The response encrypted with the transfer key of the secret, next to or instead of data.

An error entry

{
    "type": "min",
    "field": "price_cents",
    "message": "Please provide minimum '0' for 'price_cents' field.",
    "code": 400,
    "dataIndex": 2
}
Key Meaning
type The rule which failed : required, min, max, minLength, maxLength, email, enumValidation, invalidValue (a wrong type, a thrown validatorFun, a concurrency version mismatch…), schemaKeyNotFound (a key the schema does not have), schemaNotFound, unique, virtualFieldUsedInFind.
field The field concerned, with dots for nested fields.
message The text, from the message templates or your own throw, translated when the caller asks for a language.
code The HTTP status of this problem. The statusCode of the reply is the highest one.
dataIndex For a save of several objects : the index of the object in the array.
stack, apiCallSequence Present for unexpected errors of your code, to help you debug : the stack as lines, and the chain of APIs which led there.

Status codes

Code Meaning
200 Done.
201 Created : a save through save single or multiple or master save.
400 The request is wrong : validation errors, an unknown key, a bad query, a missing body.
401 The token is missing, malformed or expired. Get a new one from the token API.
403 The token is fine but no group of the API user grants this API, this table or this field ; or the origin of the browser is not allowed. Fix the group, do not sign the user out.
404 The path does not exist : a typo in the URL or a wrong method, an instance name which is not there. Also the answer of update by id, replace by id and remove by id with throwErrorIfRecordNotFound=true when the id is unknown.
500 An error of your code or of the database. The message and, for your code, the stack are in errors.

The format of the reply

The body is JSON unless the request asks otherwise with headers :

Header Effect
x-am-content-type-response application/json (default), text/xml, text/yaml, text/plain, text/html, application/octet-stream. The envelope is the same, only its encoding changes.
x-am-response-case The case of every key : camelCase, snake_case, PascalCase, kebab-case…
x-am-response-object-type: make_flat Nested objects flattened with _ between the levels : state_id_country_id_country_name.
x-am-meta: true Adds meta.
x-am-get-encrypted-data Adds or replaces data with encryptedData.
x-no-compression: true No gzip or brotli, whatever the size (replies bigger than compressThreshold, 51200 bytes by default, are compressed otherwise).

A custom API can also answer something other than the envelope : set g.res.contentType to a MIME type and return a string, or return a file, see Content types and the download example.

Response headers

Header Meaning
x-am-data-source cache when the reply came from Redis, api when the API ran.
Content-Encoding gzip or br when the reply was compressed.

In your code

The same envelope is what g.sys.db.*, g.sys.system.* and g.sys.cache.* return when you ask for the full response with the second argument true. Without it, they return data and throw the errors.

1
2
3
const full = await g.sys.db.getAll({ instance: 'mongodb', database: 'shop', collection: 'products' }, true);
if (!full.success) g.logger.error(full.errors);
const rows = full.data;