API
API is responsible for handling the actual ajax requests sent by upfront. It is a class that implements the ApiCaller interface with the required call method. It offers the following customisations if you decide to extend the service.
 requestOptions
This is an optional property on the API class which should have the value of Partial<RequestInit>. This is merged into the request if defined.
 initRequest()
This is an optional method that returns Partial<RequestInit> | Promise<Partial<RequestInit>>. It takes 3 parameters, in order the url - the endpoint to send the request to; the method - the http method; and optionally the data - which is an object literal or FormData.
 getParamEncodingOptions
When the API receives some data to send as a request, it utilises the qs package to encode the object into query parameters. This object is the configuration (qs.IStringifyOptions) object for the package. The typings for this package is an optional dependency of upfront, so you may choose not to include it in your development.
Furthermore, it was built to support sending form data. This means it accepts a FormData object, and it will configure the headers for you.
RequestInit resolving
The API prepares the RequestInit configuration in the following order:
- RequestInitcreated with the- methodargument
- Merge the requestOptionsproperty into theRequestInit
- Merge the initRequest()method's result into theRequestInit
- Merge in any headers from the GlobalConfig into the RequestInit
- Set the Content-Typeheader to the appropriate value if not already set.
- Update the url with any query parameter if needed.
- Merge in the headers from the ApiCaller'scallmethod'scustomHeadersargument into theRequestInit
- Set the Acceptheader toapplication/jsonif not already set.
Shape of the request
This part applies if you're not using a custom, customising the API service or customising the query string in the builder. Deviation from this, like adjusting the getParamEncodingOptions or implementing a custom service will cause different results. With the default settings, your api has to be ready to parse the requests with the following format.
A sample get request User.whereKey(1).get() will encode to the following:
GET https://test-api-endpoint.com/users?wheres[0][column]=id&wheres[0][operator]=%3D&wheres[0][value]=1&wheres[0][boolean]=and
Content-type: application/x-www-form-urlencoded, charset=utf-8
Accept: application/json 
Which is the equivalent of baseEndPoint + endpoint + the following object in the get parameters:
{
    wheres: [
        { column: 'id', operator: '=', value: 1, boolean: 'and' }
    ]
}
On all other requests where the body is allowed the above object will be sent in the main body.
Query types
The full typings for the possible values is the following:
type BooleanOperator = 'and' | 'or';
type Direction = 'asc' | 'desc';
type Operator = '!=' | '<' | '<=' | '=' | '>' | '>=' | 'between' | 'in' | 'like' | 'notBetween' | 'notIn';
type Order = { column: string; direction: Direction };
type WhereDescription = {
    column: string;
    operator: Operator;
    value: any;
    boolean: BooleanOperator;
};
type QueryParams = Partial<{
    wheres: WhereDescription[]; // where the row tests true these conditions
    columns: string[]; // select only these columns
    with: string[]; // return with these relations
    scopes: string[]; // apply these scopes
    relationsExists: string[]; // only return if these relations exists
    orders: Order[]; // return records in this order
    distinct: string[]; // return rows that are unique by these columns
    offset: number; // skip this many records
    limit: number; // limit the number of records to this
    page: number; // return the records from this page in pagination
}>;
Furthermore:
- The backend should be able to parse the WhereDescription['column']into the relation on the backend if the following format is found'shifts.start_time'. This example will mean that we're interested in thestart_timecolumn on theshiftsrelation.
DANGER
This is required to implement if you're using the belongsToMany method. However, if not using the belongsToMany and not using the where methods like User.where('relation.column', 1).get(), you don't have to implement.
- The backend should recognise if the withscontains the string'*'it means all relations are requested.
DANGER
This is required to implement if you're using the morphTo method. However, if not using the morphTo method, you don't have to implement.