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:
RequestInit
created with themethod
argument- Merge the
requestOptions
property into theRequestInit
- Merge the
initRequest()
method's result into theRequestInit
- Merge in any headers from the GlobalConfig into the
RequestInit
- Set the
Content-Type
header to the appropriate value if not already set. - Update the url with any query parameter if needed.
- Merge in the headers from the
ApiCaller
'scall
method'scustomHeaders
argument into theRequestInit
- Set the
Accept
header toapplication/json
if 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_time
column on theshifts
relation.
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
withs
contains 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.