Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
@lion/ajax
Advanced tools
Ajax
is a small wrapper around fetch
which:
ajax.fetchJson
by automatically serializing request body and deserializing response payload as JSON, and adding the correct Content-Type and Accept headers.npm i --save @lion/ajax
Ajax
delegates all requests to fetch. ajax.fetch
and ajax.fetchJson
have the same function signature as window.fetch
, you can use any online resource to learn more about fetch. MDN is a great start.
ajax.fetch
The fetch
method of ajax
is a very small wrapper around native window.fetch
and returns a native Response
object, the main differences with native window.fetch
are:
Ajax
classOtherwise, you can expect the same usage as from window.fetch
. Here are some simple examples:
// A simple GET request
const response = await ajax.fetch('/api/foo');
const data = await response.json(); // or .text(), .clone(), .formData(), etc
// A simple POST request
const response = await ajax.fetch('/api/foo', {
method: 'POST',
body: JSON.stringify({ foo: 'bar' }),
});
ajax.fetchJson
The fetchJson
method of ajax
has some additional features, added for convenience and ease of use. For example, the fetchJson
method:
accept
header with a value of application/json
content-type
header with a value of application/json
, if a request body is providedJSON.stringifies
the request body, if one is providedNote that instead of returning only a
Response
,fetchJson
returns an object containing theResponse
and aJSON.parse
'dbody
// A simple GET request
const { response, body } = await ajax.fetchJson('/api/foo');
// body.foo === 'bar';
// A simple POST request
const { response, body } = await ajax.fetchJson('/api/foo', {
method: 'POST',
body: { foo: 'bar' },
});
Interceptors are functions that can be used to inspect or modify the Request
or Response
objects of a network request.
A request interceptor is a function that takes a Request
object, and returns a Request
object, or a Response
object, and runs before the native window.fetch
call is done, allowing you to modify or inspect a request before it's made.
If you return a Response
object, the response will be returned by the fetch
or fetchJson
methods, instead of passing the Request
to the native window.fetch
function.
Returning a Request
:
function addAcceptLanguage(request) {
request.headers.set('accept-language', 'EN_GB');
return request;
}
ajax.addRequestInterceptor(addAcceptLanguage);
Returning a Response
:
function interceptFooRequest(request) {
if (request.headers.get('foo')) {
return Response.json({ foo: 'bar' });
}
return request;
}
ajax.addRequestInterceptor(interceptFooRequest);
Request interceptors can be async and will be awaited.
A response interceptor is a function that takes a Response
object, and returns a Response
object, and runs after the native window.fetch
call is done, allowing you to modify or inspect the response before it's returned by fetch
/fetchJson
.
async function rewriteFoo(response) {
const body = await response.clone().text();
return new Response(body.replaceAll('foo', 'bar'), response);
}
ajax.addResponseInterceptor(rewriteFoo);
Response interceptors can be async and will be awaited.
A response JSON object interceptor is a function that takes a successfully parsed response JSON object and response
object and returns a new response JSON object.
It's used only when the request is made with the fetchJson
method, providing a convenience API to directly modify or inspect the parsed JSON without the need to parse it and handle errors manually.
async function interceptJson(jsonObject, response) {
if (response.url === '/my-api') {
return {
...jsonObject,
changed: true,
};
}
return jsonObject;
}
ajax.addResponseJsonInterceptor(interceptJson);
Response JSON object interceptors can be async and will be awaited.
Property | Type | Default Value | Description |
---|---|---|---|
addAcceptLanguage | boolean | true | Whether to add the Accept-Language header from the data-localize-lang document property |
addCaching | boolean | false | Whether to add the cache interceptor and start storing responses in the cache, even if cacheOptions.useCache is false |
xsrfCookieName | string | "XSRF-TOKEN" | The name for the Cross Site Request Forgery cookie |
xsrfHeaderName | string | "X-XSRF-TOKEN" | The name for the Cross Site Request Forgery header |
xsrfTrustedOrigins | string[] | [] | List of trusted origins, the XSRF header will also be added if the origin is in this list. |
jsonPrefix | string | "" | The prefix to add to add to responses for the .fetchJson functions |
cacheOptions.useCache | boolean | false | Whether to use the default cache interceptors to cache requests |
cacheOptions.getCacheIdentifier | function | a function returning the string _default . | A function to determine the cache that should be used for each request; used to make sure responses for one session are not used in the next. Can be async. |
cacheOptions.methods | string[] | ["get"] | The HTTP methods to cache reponses for. Any other method will invalidate the cache for this request, see "Invalidating cache", below |
cacheOptions.maxAge | number | 360000 | The time to keep a response in the cache before invalidating it automatically |
cacheOptions.invalidateUrls | string[] | undefined | Urls to invalidate each time a method not in cacheOptions.methods is encountered, see "Invalidating cache", below |
cacheOptions.invalidateUrlsRegex | regex | undefined | Regular expression matching urls to invalidate each time a method not in cacheOptions.methods is encountered, see "Invalidating cache", below |
cacheOptions.requestIdFunction | function | a function returning the base url and serialized search parameters | Function to determine what defines a unique URL |
cacheOptions.contentTypes | string[] | undefined | Whitelist of content types that will be stored to or retrieved from the cache |
cacheOptions.maxResponseSize | number | undefined | The maximum response size in bytes that will be stored to or retrieved from the cache |
cacheOptions.maxCacheSize | number | undefined | The maxiumum total size in bytes of the cache; when the cache gets larger it is truncated |
import { ajax, createCacheInterceptors } from '@lion/ajax';
// Note: getCacheIdentifier can be async
const getCacheIdentifier = () => {
let userId = localStorage.getItem('lion-ajax-cache-demo-user-id');
if (!userId) {
localStorage.setItem('lion-ajax-cache-demo-user-id', '1');
userId = '1';
}
return userId;
};
const TEN_MINUTES = 1000 * 60 * 10; // in milliseconds
const cacheOptions = {
useCache: true,
maxAge: TEN_MINUTES,
};
const [cacheRequestInterceptor, cacheResponseInterceptor] = createCacheInterceptors(
getCacheIdentifier,
cacheOptions,
);
ajax.addRequestInterceptor(cacheRequestInterceptor);
ajax.addResponseInterceptor(cacheResponseInterceptor);
Or use a custom cache object and add the cache config to the constructor:
import { Ajax } from '@lion/ajax';
const storeButDontRetrieveByDefaultConfig = {
addCaching: true,
cacheOptions: {
getCacheIdentifier,
useCache: false,
maxAge: TEN_MINUTES,
},
};
const customAjax = new Ajax(storeButDontRetrieveByDefaultConfig);
Invalidating the cache, or cache busting, can be done in multiple ways:
maxAge
of the cache objectinvalidatesUrls
and invalidateUrlsRegex
The library has a number of options available to restrict what should be cached. They include:
cacheOptions.contentTypes
If this option is set, it is interpreted as a whitelist for which content types to cache. The content types of a given
response is derived from its Content-Type
header. If this option is set, responses that do not have a Content-Type
header are never added to or retrieved from the cache.
cacheOptions.maxResponseSize
This option sets a maximum size (in bytes) for a single response to be cached. The size of the response is determined first by looking
at the Content-Length
header; if this header is not available, the response is inspected (through the blob()
function)
and its size retrieved.
cacheOptions.maxCacheSize
This option sets a maximum size (in bytes) for the whole cache. The size of a response is determined first by looking
at the Content-Length
header; if this header is not available, the response is inspected (through the blob()
function)
and its size retrieved.
If the cache grows larger than the maxCacheSize
option, the cache is truncated according to a First-In-First-Out
(FIFO) algorithm that simply removes the oldest entries until the cache is smaller than options.maxCacheSize
.
FAQs
Thin wrapper around fetch with support for interceptors.
We found that @lion/ajax demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.