Request Options
To init a rest client you need a base object describing behaviors and details, that every request method will inherits:
// Functional API
const client = createRestClient({
host: "https://server.com",
responseType: "text"
})
// Class API
const client = new RestClient({
host: "https://server.com",
responseType: "text"
})
Options provided at this level are considered as Global Layer options.
Every option can also be overridden at the request method's level:
const response = client.get("/path", { // << the options object
responseType: "json"
})
At this level options are considered Local Layer options.
TIP
You should play with overrideStrategy to better understand the magic behind overrides.
Standard options
Standard properties from original fetch's Request object are supported and usable as options object, except for a small group of props that were overridden or disabled to achieve Scarlett's related abstractions, for example the standard url property that we are using as first parameter in majority of cases and defining it as string
type.
If you are curious about the list of supported options from the standard interface, try to import the following type:
import { AllowedNativeOptions } from "scarlett"
const playWithIt: AllowedNativeOptions; // << inspect this type!
Refer to the official documentation about how they works, continue reading about Scarlett's built-in options.
host
type host = string
Defaults to localhost.href
.
basePath
type basePath = string
The base path to use on every request, defaults to /
, combined with the host
option.
responseType
type responseTypeRaw = "json" | "text" | "blob" | "arrayBuffer" | "formData"
type responseType = responseTypeRaw
| (request: IRequest, fetchResponse: Response | null) => responseTypeRaw
| (request: IRequest, fetchResponse: Response | null) => Promise<responseTypeRaw>
| undefined | null;
This property will lead the response body parsing, to get the proper output type. For example, with json
as responseType you don't need to JSON.parse()
on response.data
.
It can be defined as:
responseTypeRaw
- A sync method returning a
responseTypeRaw
ts(request: IRequest, fetchResponse: Response | null) => HttpResponseFormatType
- An async method resolving a
responseTypeRaw
ts(request: IRequest, fetchResponse: Response | null) => Promise<HttpResponseFormatType>
When the value resolved is undefined
or null
, the response's body will not be parsed.
Defaults to undefined
.
TIP
Use the sync/async handler to handle complex business logics affecting the response body type.
body
type Body = Record<string, any> | string | ArrayBuffer | ArrayBufferView | Blob | File | FormData | undefined
Optional request body content, if the method is GET
, this value will be set to undefined
.
query
type query = Record<string, any>
Optional key-value pair, this will be converted (and appended) to the request URI.
queryParamsTransformer
interface IQueryParamTransformer {
(key: string, value: any, query: any): string
}
Let's suppose you have a complex key-value pair, in which every value needs to be converted using a custom logic. In this case you can use this handler to convert your parameters to string
.
queryParamsIncludeEmpty
type queryParamsIncludeEmpty = boolean
If true, it will include falsy values as empty, example: /example/?a=&b=
.
Defaults to false
.
cacheInMemory
type cacheInMemory = boolean
If true, it will enable an internal, Map based, cache system. Every entry for this cache, will use a compound-key containing the cacheKey
, if provided. See the cache section for more details.
Defaults to false
.
TIP
Once enabled, it can works together with the Standard Fetch API's cache mechanism (link). Keep in mind that if the Standard Cache mechanism will run only if the in memory cache haven't any cached record, in other words, the built-in cache system has the priority.
cacheExpireIn
type cacheExpireIn = number | undefined
Define the cache entry expiry in milliseconds.
Defaults to undefined
, meaning that will never expire.
cacheKey
type cacheKey = string
An optional alias reference to the current request, useful if you are using cacheInMemory
parameter as true.
Keep in mind that when the in-memory cache is activated, a base cacheKey will be evaluated and used to get/set internal operations on cache store. The base cacheKey is basically a compound key having the following serialized info:
- Absolute url
- HTTP Method
- Serialized inputs (for POST and PUT requests)
Defining this parameter doesn't override the entire overall base cacheKey, your cacheKey string will be prepended to the base one, in this way Scarlett grants cache items isolation per request.
Defaults to ""
.
throw
type throwOption = boolean
As standard behavior of fetch, every request will never throw error. But sometimes, in very large applications, you need a centralized API error handler. If true
, when the standard fetch -> Response.ok is false the API will throw an error. The error object will be an instance of RestError class.
Defaults to false
.
throwExcluding
type ResponseFilter = {
path?: string;
method?: HttpMethod;
statusCode?: HTTPStatusCode;
errorCode?: InternalErrorCode;
} || {
(restError: RestError<TError>): boolean
} || {
(restError: RestError<TError>): Promise<boolean>
}
Even when you throwing error on failed requests, sometimes you may need to filter this errors and react properly without throwing.
You can do this providing an array of ResponseFilter
.
A filter can be defined as object (every prop is optional):
await client.get("/example", {
throwExcluding: [{
path: "/example", // the url path to exclude
method: "GET", // the method to exclude
statusCode: 404, // the status code to exclude
errorCode: "Timeout" // the internal error code to exclude
}]
})
The property errorCode
differs from statusCode
, because it is related to library's internal mechanisms errors. The type definition is:
type InternalErrorCode = "Timeout" | "BodyParse" | "UrlParameter";
- Timeout, when a request fails to a timeout
- BodyParse, when the body parse fails with the given
responseType
- UrlParameter, when something gets wrong during set or get operations on url parameters
You can even declare it as sync/async method returning true
to prevent the throw
:
await client.get("/example", {
throwExcluding: [
async (err) => {
let willPreventError = true;
// ...awaitable methods here...
return willPreventError;
}
]
})
If a failed request match one of the items provided here, your rest client instance will not throw any error.
You will find the matched filter on response.throwFilter property.
Setting throwExcluding
will also set implicitly set throw
option to true
.
overrideStrategy
type overrideStrategy = "merge" | "assign"
Set the override strategy used when overriding Global Layer options in a rest method (Local Layer).
Available strategies:
- merge (default), every simple primitive type (like strings, and numbers) will be overwritten, while Headers, Object-like and Array-like options will be merged.
- assign, every value will be overwritten using Object.assign().
onRequest
type onRequest = (request: IRequest) => void | Promise
Global handler, running on your rest client context, called at every request. You can edit the outgoing request options just modifying the request
object provided as first argument. If the return value is a Promise
's instance, the request will await
for it before starting.
onResponse
type onResponse = (response: IResponse) => void
Global handler, running on your rest client context, called at every successful response received. Keep in mind that, if you set the throw
option as true, or any of your throwExcluding
filters doesn't match, this handler will never be called.
onError
type onError = (error: RestError, response: IResponse) => void
Global handler, running on your rest-client context, called every time an error was received by a request. This callback will not be invoked if it is filtered by throwExcluding
option.