Ajax
ajax
is a small wrapper around fetch
which:
- Allows globally registering request and response interceptors
- Throws on 4xx and 5xx status codes
- Prevents network request if a request interceptor returns a response
- Supports a JSON request which automatically encodes/decodes body request and response payload as JSON
- Adds accept-language header to requests based on application language
- Adds XSRF header to request if the cookie is present
How to use
Installation
npm i --save @lion/ajax
Relation to fetch
ajax
delegates all requests to fetch. ajax.request
and ajax.requestJson
have the same function signature as window.fetch
, you can use any online resource to learn more about fetch. MDN is a great start.
Example requests
GET request
import { ajax } from '@lion/ajax';
const response = await ajax.request('/api/users');
const users = await response.json();
POST request
import { ajax } from '@lion/ajax';
const response = await ajax.request('/api/users', {
method: 'POST',
body: JSON.stringify({ username: 'steve' }),
});
const newUser = await response.json();
JSON requests
We usually deal with JSON requests and responses. With requestJson
you don't need to specifically stringify the request body or parse the response body:
GET JSON request
import { ajax } from '@lion/ajax';
const { response, body } = await ajax.requestJson('/api/users');
POST JSON request
import { ajax } from '@lion/ajax';
const { response, body } = await ajax.requestJson('/api/users', {
method: 'POST',
body: { username: 'steve' },
});
Error handling
Different from fetch, ajax
throws when the server returns a 4xx or 5xx, returning the request and response:
import { ajax } from '@lion/ajax';
try {
const users = await ajax.requestJson('/api/users');
} catch (error) {
if (error.response) {
if (error.response.status === 400) {
} else {
console.error(error);
}
} else {
console.error(error);
}
}
Ajax Cache
A caching library that uses lion-web/ajax
and adds cache interceptors to provide caching for use in
frontend services
.
Technical documentation and decisions can be found in
./docs/technical-docs.md
Getting started
Consume the global ajax
instance and add the interceptors to it, using a cache configuration
which is applied on application level. If a developer wants to add specifics to cache behavior
they have to provide a cache config per action (get
, post
, etc.) via cacheOptions
field of local ajax config,
see examples below.
Note: make sure to add the interceptors only once. This is usually
done on app-level
import {
ajax,
cacheRequestInterceptorFactory,
cacheResponseInterceptorFactory,
} from '@lion-web/ajax.js';
const globalCacheOptions = {
useCache: true,
timeToLive: 1000 * 60 * 5,
};
const getCacheIdentifier = () => getActiveProfile().profileId;
ajax.addRequestInterceptor(cacheRequestInterceptorFactory(getCacheIdentifier, globalCacheOptions));
ajax.addResponseInterceptor(
cacheResponseInterceptorFactory(getCacheIdentifier, globalCacheOptions),
);
const { response, body } = await ajax.requestJson('/my-url');
Ajax cache example
import {
ajax,
cacheRequestInterceptorFactory,
cacheResponseInterceptorFactory,
} from '@lion-web/ajax';
const getCacheIdentifier = () => getActiveProfile().profileId;
const globalCacheOptions = {
useCache: false,
timeToLive: 50,
methods: ['get'],
};
class TodoService {
constructor() {
this.localAjaxConfig = {
cacheOptions: {
invalidateUrls: ['/api/todosbykeyword'],
},
};
}
getTodos() {
return ajax.requestJson(`/api/todos`, this.localAjaxConfig);
}
getTodosByKeyword(keyword) {
return ajax.requestJson(`/api/todosbykeyword/${keyword}`, this.localAjaxConfig);
}
saveTodo(todo) {
return ajax.requestJson(`/api/todos`, { method: 'POST', body: todo, ...this.localAjaxConfig });
}
}
If a value returned by cacheIdentifier
changes the cache is reset. We avoid situation of accessing old cache and proactively clean it, for instance when a user session is ended.
Ajax cache Options
const cacheOptions = {
useCache: true,
timeToLive: 1000 * 60 * 5,
methods: ['get'],
invalidateUrls: ['/api/todosbykeyword'],
invalidateUrlsRegex: /posts/
requestIdentificationFn: (request, serializer) => {
return `${request.url}?${serializer(request.params)}`;
},
};
Considerations
Fetch Polyfill
For IE11 you will need a polyfill for fetch. You should add this on your top level layer, e.g. your application.
This is the polyfill we recommend. It also has a section for polyfilling AbortController