-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #206 from infinum/feature/network
Network implementation
- Loading branch information
Showing
94 changed files
with
4,506 additions
and
192 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
--- | ||
id: network-mixin | ||
title: Network Mixin | ||
--- | ||
|
||
If you're using an API for your application, you can install `datx-network` to take the full advantage of the `datx` library: | ||
|
||
```bash | ||
npm install --save datx datx-network mobx | ||
``` | ||
|
||
**Note** If you're using the [JSON API specification](https://jsonapi.org/), check out [datx-jsonapi](./jsonapi-mixin) instead. | ||
|
||
## Polyfilling | ||
|
||
The lib makes use of the following features that are not yet available everywhere. Based on your browser support, you might want to polyfill them: | ||
|
||
- [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) | ||
- [Object.assign](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) | ||
- [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) | ||
- [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) | ||
|
||
## Documentation | ||
|
||
- [BaseRequest](../network/base-request) | ||
- [Response](../network/response) | ||
- [operators](../network/operators) | ||
- [caching](../network/caching) | ||
- [interceptors](../network/interceptors) | ||
- [parse/serialize](../network/parse-serialize) | ||
- [fetching](../network/fetching) | ||
- [TypeScript Interfaces](../network/typescript-interfaces) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
--- | ||
id: base-request | ||
title: BaseRequest | ||
--- | ||
|
||
BaseRequest is the main class to create a new request pipeline. | ||
|
||
# constructor | ||
|
||
```typescript | ||
constructor(baseUrl: string): BaseRequest; | ||
``` | ||
|
||
The constructor has one mandatory argument and that's the base url. | ||
|
||
# pipe | ||
|
||
```typescript | ||
pipe< | ||
TNewModel extends PureModel | Array<PureModel> = TModel, | ||
TNewParams extends object = TParams | ||
>(...operators: Array<IPipeOperator>): BaseRequest<TNewModel, TNewParams>; | ||
``` | ||
The pipe method is used to configure the requests. It will always create a clone of the current base request and modify the clone. | ||
To see what's possible to configure, check out the [operators](./operators) page. | ||
This method can receive two types in the generic. The first one is the expected return type of the `fetch` method. This can be either a model type or an array of models. The second type is a `Record` defining which params are expected in the fetch method - this should match the `setUrl` placeholders that weren't defined in the `params` operator. | ||
# clone | ||
```typescript | ||
clone<TNewModel extends PureModel = TModel, TNewParams extends object = TParams>( | ||
BaseRequestConstructor?: typeof BaseRequest, | ||
): BaseRequest<TNewModel, TNewParams>; | ||
``` | ||
This method clones the current BaseRequest class instance. Since it's possible to use custom BaseRequest classes that extend the original one, the clone method will make sure the correct class is used. If needed, you can also pass a different BaseClass implementation. | ||
# fetch | ||
```typescript | ||
fetch(params?: TParams): Promise<Response<TModel>>; | ||
``` | ||
The fetch method will make the API request and either return a [`Response`](./response) with the `data` property if successful or throw a `Response` with an `error` property. | ||
# useHook | ||
```typescript | ||
useHook( | ||
params?: TParams, | ||
options?: { suspense: boolean }, | ||
): [Response<TModel> | null, boolean, string | Error | null]; | ||
``` | ||
This method is a React hook. It can receive two arguments - the first one is the params object, and the second one is a hook options object. The only option supported right now is a `suspense` flag that will work with the [React suspense for data fetching](https://reactjs.org/docs/concurrent-mode-suspense.html). | ||
The return value is an array consisting of three items: The first one is a `Response` that will contain either the `data` or `error` property. The second value is a boolean loading flag, and the third one is an error value that will either be `null`, a string or an `Error` object. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
--- | ||
id: caching | ||
title: Caching | ||
--- | ||
|
||
The library can use multiple caching strategies in order to optimize your network communication. The caching works only for `GET` requests and it can be controlled by using the [`cache` operator](./operators). | ||
|
||
Supported caching strategies are: | ||
|
||
```typescript | ||
enum CachingStrategy { | ||
NetworkOnly = 1, // Ignore cache | ||
NetworkFirst = 2, // Fallback to cache only on network error | ||
StaleWhileRevalidate = 3, // Use cache and update it in background | ||
CacheOnly = 4, // Fail if nothing in cache | ||
CacheFirst = 5, // Use cache if available | ||
StaleAndUpdate = 6, // Use cache and update response once network is complete | ||
} | ||
``` | ||
|
||
The default caching strategy in the browser is `CachingStrategy.CacheFirst` and on the server it's `CachingStrategy.NetworkOnly`. | ||
|
||
Besides the caching strategy, you can also set the `maxAge` value, which is a number of seconds a response will be cached for. The default maxAge is `Infinity`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
id: fetching | ||
title: Fetching TODO | ||
--- | ||
|
||
# TODO |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
--- | ||
id: getting-started | ||
title: Getting started with DatX Network | ||
--- | ||
|
||
# Getting started | ||
|
||
Note: `datx-network` has a peer dependency to `mobx@^4.2.0` or `mobx@^5.5.0`, so don't forget to install the latest MobX version: | ||
|
||
```bash | ||
npm install --save datx-network mobx | ||
``` | ||
|
||
## Polyfilling | ||
|
||
The lib makes use of the following features that are not yet available everywhere. Based on your browser support, you might want to polyfill them: | ||
|
||
- [Symbol.for](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol) | ||
- [Object.assign](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) | ||
- [Array.prototype.find](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find) | ||
- [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) | ||
- [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) | ||
|
||
[How to add the polyfills](https://datx.dev/docs/troubleshooting/known-issues#the-library-doesnt-work-in-internet-explorer-11). | ||
Note: Fetch API is not included in the polyfills mentioned in the Troubleshooting page. Instead, you need to add it as a separate library. If you don't have any special requirements (like server-side rendering), you can use the [window.fetch polyfill](https://github.com/github/fetch#installation). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
--- | ||
id: interceptors | ||
title: Interceptors TODO | ||
--- | ||
|
||
The library is using a concept of interceptors to handle use cases like authentication or other flows. This is conceptually similar to [Angular interceptors](https://angular.io/api/common/http/HttpInterceptor) or [Express middleware](https://expressjs.com/en/guide/using-middleware.html). | ||
|
||
THe interceptors are responsible for most of the flow once the fetch method is called - including caching and doing the actual API request. | ||
|
||
The interceptors receive the request object ([`IFetchOptions`](./typescript-ionterfaces#ifetchoptions)) and an optional `next` function representing the next interceptor in the chain. The return value of every interceptor should be a promise of the response object. | ||
|
||
Only the bottom interceptor won't get the next function and that means that it will be responsible for making the actual API call. | ||
|
||
## Call order | ||
|
||
If we execute the following code: | ||
|
||
```typescript | ||
request | ||
.pipe(addInterceptor(interceptorA), addInterceptor(interceptorB), addInterceptor(interceptorC)) | ||
.fetch(); | ||
``` | ||
|
||
the call stack will look like this: | ||
|
||
![Interceptor stacking diagram](../assets/datx-network-interceptors.svg) | ||
|
||
Basically, the last added interceptor will be the first one called, and the last one getting the response back. | ||
|
||
## Builtin interceptors | ||
|
||
The library contains two builtin interceptors that try to make lib usage as simple as possible. However, in order to make things as flexible as possible, you can either completely remove or replace any of the interceptors with your implementation. | ||
|
||
Bellow is the description of the builtin interceptors and what to do if you want to replace them. | ||
|
||
**Note:** If you replace one of the builtin interceptors, make sure you don't use any of the operators mentioned bellow, as they will restore the original interceptor. | ||
|
||
### Fetch | ||
|
||
The fetch interceptor is responsible for making the request. It has three configurable options, each one can be modified by using one of the operators. | ||
|
||
Options: | ||
|
||
- fetch reference - this needs to be defined as `window.fetch` or any other compatible function, like the one from the `isomorphic-fetch` library. In the browser, the reference will be automatically set to `window.fetch` if it exists. If you're running the lib outside of the main browser process or the browser might not support the Fetch API, then you can set your own fetch implementation by using the [`fetchReference` operator](./operators#fetchreference). | ||
- serializer - this function prepares the request body for the API call. This might be something like transforming a model snapshot to a format supported by the API. The function can be defined by using the [`serializer` operator](./operators#serializer) | ||
- parser - this function prepares the API response for the [`Response`](./response) initialization. This might be e.g. because the response objects need to be modified or because the response is nested in a way that's not compatible with the `Response` class. | ||
|
||
### Cache | ||
|
||
The cache interceptor is responsible for caching of the network request. This implementation will cache only the `GET` requests. The interceptor supports two options described bellow, and they can be changed by using the [`cache` operator](./operators#cache). | ||
|
||
Options: | ||
|
||
- The default caching strategy in the browser is `CachingStrategy.CacheFirst` and on the server it's `CachingStrategy.NetworkOnly`. | ||
- Besides the caching strategy, you can also set the `maxAge` value, which is a number of seconds a response will be cached for. The default maxAge is `Infinity`. | ||
|
||
You can find more about the caching and configuration details on the [caching page](./caching). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
--- | ||
id: operators | ||
title: Operators | ||
--- | ||
|
||
The operators are used to configure the [base request](./base-request). | ||
|
||
## setUrl | ||
|
||
```typescript | ||
function setUrl(url: string, type: IType | typeof PureModel = PureModel); | ||
``` | ||
|
||
Set the endpoint that should be loaded and the model that should be initialized. The url can contain placeholders that will be filled by using the `params` operator or as a fetch argument. | ||
The url will be appended to the base url. An example with placeholders: `/articles/{articleId}`. | ||
|
||
## addInterceptor | ||
|
||
```typescript | ||
function addInterceptor(interceptor: IInterceptor, name?: string); | ||
``` | ||
|
||
The library is using a concept of interceptors to handle use cases like authentication or other flows. This is conceptually similar to [Angular interceptors](https://angular.io/api/common/http/HttpInterceptor) or [Express middleware](https://expressjs.com/en/guide/using-middleware.html). | ||
|
||
The interceptors can have an optional name so it's easier to manipulate them in later stages. The default name, if none given, will be the given function name. | ||
|
||
To find more about interceptors, check out the [interceptors](./interceptors) page. | ||
|
||
## upsertInterceptor | ||
|
||
```typescript | ||
function upsertInterceptor(interceptor: IInterceptor, name?: string); | ||
``` | ||
|
||
Replace the interceptor with the given name. The new interceptor will be placed in the same place in the order as the old interceptor. | ||
|
||
## removeInterceptor | ||
|
||
```typescript | ||
function removeInterceptor(name: string); | ||
``` | ||
|
||
Remove the interceptor with the given name. | ||
|
||
## cache | ||
|
||
```typescript | ||
function cache(strategy: CachingStrategy, maxAge?: number); | ||
``` | ||
|
||
The library supports multiple caching strategies. Only the GET request can be cached and you can configure the behavior by using the `cache` operator. The operator receives a strategy and the max age. | ||
The default caching strategy in the browser is `CachingStrategy.CacheFirst` and on the server it's `CachingStrategy.NetworkOnly`. The default maxAge is `Infinity`. | ||
|
||
To find out more about the caching strategies, check out the [caching](./caching) page. To implement your custom caching strategy, check out the [interceptors](./interceptors) page about how to replace builtin interceptors. | ||
|
||
_Note:_ Used only with the [built in cache interceptor](./interceptors#cache). | ||
|
||
## method | ||
|
||
```typescript | ||
function method(method: HttpMethod); | ||
``` | ||
|
||
Set the HTTP method that should be used for the request. | ||
|
||
## body | ||
|
||
```typescript | ||
function body(body: any, bodyType?: BodyType); | ||
``` | ||
|
||
To send a body with your request, use the `body` operator. As a first argument, it receives the payload to be send. If the second argument is not set, the library will try to discover what type should be used. The body type is used for the payload serialization and `Content-Type` header value. | ||
|
||
## query | ||
|
||
```typescript | ||
function query(name: string, value: string | Array<string> | object); | ||
``` | ||
|
||
With this operator you can add query parameters to your url. The parameters can also be objects and arrays, and the `paramArrayType` operator can be used to set the way the query params will be serialized. | ||
|
||
## header | ||
|
||
```typescript | ||
function header(name: string, value: string); | ||
``` | ||
|
||
Set the HTTP headers that should be used for the request. Calling it multiple times with the same name will override the value. | ||
|
||
## params | ||
|
||
```typescript | ||
function params(name: string, value: string): (pipeline: BaseRequest) => void; | ||
function params(params: Record<string, string>): (pipeline: BaseRequest) => void; | ||
``` | ||
|
||
Set the parameters that will be set in the url. Calling it multiple times with the same name will override the value. | ||
|
||
## fetchReference | ||
|
||
```typescript | ||
function fetchReference(fetchReference: typeof fetch); | ||
``` | ||
|
||
Set the reference to the fetch method. When running in the browser, this will default to `window.fetch`, while there will be no default for the server. To make the network work across server and client, you can use a library like `isomorphic-fetch`. | ||
|
||
_Note:_ Used only with the [built in fetch interceptor](./interceptors#fetch). | ||
|
||
## encodeQueryString | ||
|
||
```typescript | ||
function encodeQueryString(encodeQueryString: boolean); | ||
``` | ||
|
||
By default, all query strings added by using the `query` operator will be url encoded. If needed, you can disable the encoding with this operator. | ||
|
||
## paramArrayType | ||
|
||
```typescript | ||
function paramArrayType(paramArrayType: ParamArrayType); | ||
``` | ||
|
||
Since the `query` operator params can be complex objects and there are multiple ways to serialize them, this option defines a desired way to do it. The default value is `ParamArrayType.ParamArray` and the possible options are: | ||
|
||
```typescript | ||
export enum ParamArrayType { | ||
MultipleParams, // filter[a]=1&filter[a]=2 | ||
CommaSeparated, // filter[a]=1,2 | ||
ParamArray, // filter[a][]=1&filter[a][]=2 | ||
} | ||
``` | ||
|
||
## serializer | ||
|
||
```typescript | ||
function serializer(serialize: (request: IFetchOptions) => IFetchOptions); | ||
``` | ||
|
||
Prepare the body of the request for sending. The default serializer just passes the unmodified data argument. | ||
|
||
_Note:_ Used only with the [built in fetch interceptor](./interceptors#fetch). | ||
|
||
## parser | ||
|
||
```typescript | ||
function parser(parse: (data: object, response: IResponseObject) => object); | ||
``` | ||
|
||
Parse the API response before data initialization. The function also receives other data that could be useful for parsing. An example use case for the parser is if all your API response is wrapped in a `data` object or something similar. | ||
|
||
_Note:_ Used only with the [built in fetch interceptor](./interceptors#fetch). | ||
|
||
## collection | ||
|
||
```typescript | ||
function collection(collection?: PureCollection); | ||
``` | ||
|
||
Link the base request to a specific DatX collection. By using this, all the models received by the API response will be added to the specified collection. This is also required in order to use references inside of your models. | ||
|
||
## Custom operators | ||
|
||
You can also create custom operators that work with the base request. The operator needs to follow the `IPipeOperator` typing. | ||
|
||
```typescript | ||
type IPipeOperator = (request: BaseRequest) => void; | ||
``` | ||
|
||
For now, no options of the base request are publicly exposed, but they might be in future. If you have some specific requests, please open an issue with the suggestion. |
Oops, something went wrong.