Axiod
Promise based HTTP client for Deno inspired by axios
Features
- Make http requests from node.js
- Supports the Promise API
- Intercept request and response
- Transform request and response data
- Automatic transforms for JSON data
- Cancel requests [Waiting for deno support of Fetch Abort using signal and AbortController]
Usage
import axiod from "https://deno.land/x/axiod/mod.ts";
axiod.get("https://google.fr").then((response) => {
// response
});
You can use type generics with Axiod
import axiod from "https://deno.land/x/axiod/mod.ts";
const { data } = await axiod<{ delay: string }>(
"https://postman-echo.com/delay/2"
);
// data type would be
// {delay: string}
Performing a GET
request
import axiod from 'https://deno.land/x/axiod/mod.ts';
axiod
.get('/user?ID=12345')
.then((response) => {
// handle success
console.log(response);
})
.catch((error) => {
// handle error
console.log(error);
})
.then(() => {
// always executed
});
// Optionally the request above could also be done as
axiod
.get('/user', {
params: {
ID: 12345,
},
})
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error);
})
.then(() => {
// always executed
});
// Want to use async/await? Add the `async` keyword to your outer function/method.
const getUser = () => {
try {
const response = await axiod.get('/user?ID=12345');
console.log(response);
} catch (error) {
console.error(error);
}
};
Performing a POST
request
import axiod from "https://deno.land/x/axiod/mod.ts";
axiod
.post("/user", {
firstName: "Fred",
lastName: "Flintstone",
})
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error);
});
Performing multiple concurrent requests
import axiod from "https://deno.land/x/axiod/mod.ts";
const getUserAccount = () => {
return axiod.get("/user/12345");
};
const getUserPermissions = () => {
return axiod.get("/user/12345/permissions");
};
Promise.all([getUserAccount(), getUserPermissions()]).then((results) => {
const acct = results[0];
const perm = results[1];
});
Axiod API
Requests can be made by passing the relevant config to axiod.
axiod(config) // Send a POST request
import axiod from "https://deno.land/x/axiod/mod.ts";
axiod({
method: "post",
url: "/user/12345",
data: {
firstName: "Fred",
lastName: "Flintstone",
},
});
import axiod from "https://deno.land/x/axiod/mod.ts";
// GET request for remote image in node.js
axiod({
method: "get",
url: "http://bit.ly/2mTM3nY",
responseType: "stream",
}).then((response) => {
response.data.pipe(fs.createWriteStream("ada_lovelace.jpg"));
});
axiod(url[, config]) // Send a GET request (default method)
import axiod from "https://deno.land/x/axiod/mod.ts";
axiod("/user/12345");
Request method aliases
For convenience aliases have been provided for all supported request methods.
axiod.request(config)
axiod.get(url[, config])
axiod.delete(url[, config])
axiod.head(url[, config])
axiod.options(url[, config])
axiod.post(url[, data[, config]])
axiod.put(url[, data[, config]])
axiod.patch(url[, data[, config]])
NOTE
When using the alias methods url, method, and data properties don’t need to be specified in config.
Creating an instance
You can create a new instance of axiod with a custom config.
import axiod from "https://deno.land/x/axiod/mod.ts";
// axiod.create([config]);
const instance = axiod.create({
baseURL: "https://some-domain.com/api/",
timeout: 1000,
headers: { "X-Custom-Header": "foobar" },
});
Request Config
These are the available config options for making requests. Only the url is required. Requests will default to GET if method is not specified.
{
// `url` is the server URL that will be used for the request
url: '/user',
// `method` is the request method to be used when making the request
method: 'get', // default
// `baseURL` will be prepended to `url` unless `url` is absolute.
// It can be convenient to set `baseURL` for an instance of axiod to pass relative URLs
// to methods of that instance.
baseURL: 'https://some-domain.com/api/',
// `headers` are custom headers to be sent
headers: {'X-Requested-With': 'XMLHttpRequest'},
// `params` are the URL parameters to be sent with the request
// Must be a plain object or a URLSearchParams object
params: {
ID: 12345
},
// `paramsSerializer` is an optional function in charge of serializing `params`
paramsSerializer: function (params) {
return Qs.stringify(params, {arrayFormat: 'brackets'})
},
// `data` is the data to be sent as the request body
// Only applicable for request methods 'PUT', 'POST', 'DELETE , and 'PATCH'
// When no `transformRequest` is set, must be of one of the following types:
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - Browser only: FormData, File, Blob
// - Node only: Stream, Buffer
data: {
firstName: 'Fred'
},
// syntax alternative to send data into the body
// method post
// only the value is sent, not the key
data: 'Country=Brasil&City=Belo Horizonte',
// `timeout` specifies the number of milliseconds before the request times out.
// If the request takes longer than `timeout`, the request will be aborted.
timeout: 1000, // default is `0` (no timeout)
// `withCredentials` indicates whether or not cross-site Access-Control requests
// should be made using credentials
withCredentials: false, // default
// `auth` indicates that HTTP Basic auth should be used, and supplies credentials.
// This will set an `Authorization` header, overwriting any existing
// `Authorization` custom headers you have set using `headers`.
// Please note that only HTTP Basic auth is configurable through this parameter.
// For Bearer tokens and such, use `Authorization` custom headers instead.
auth: {
username: 'janedoe',
password: 's00pers3cret'
}
// `responseType` indicates the type of data that the server will respond with
// options are: 'arraybuffer', 'json', 'text', 'stream'
// browser only: 'blob'
// Check __tests__/response-type.ts for some examples
responseType: 'json', // default
// `validateStatus` defines whether to resolve or reject the promise for a given
// HTTP response status code. If `validateStatus` returns `true` (or is set to `null`
// or `undefined`), the promise will be resolved; otherwise, the promise will be
// rejected.
validateStatus: function (status) {
return status >= 200 && status < 300; // default
},
// `transformRequest` allows changes to the request data before it is sent to the server
// This is only applicable for request methods 'PUT', 'POST', 'PATCH' and 'DELETE'
// The last function in the array must return a string or an instance of Buffer, ArrayBuffer,
// FormData or Stream
// You may modify the headers object.
transformRequest: [function (data, headers) {
// Do whatever you want to transform the data
return data;
}],
// `transformResponse` allows changes to the response data to be made before
// it is passed to then/catch
transformResponse: [function (data) {
// Do whatever you want to transform the data
return data;
}],
}
Response Schema
The response for a request contains the following information.
{
// `data` is the response that was provided by the server
data: {},
// `status` is the HTTP status code from the server response
status: 200,
// `statusText` is the HTTP status message from the server response
statusText: 'OK',
// `headers` the HTTP headers that the server responded with
// All header names are lower cased and can be accessed using the bracket notation.
// Example: `response.headers['content-type']`
headers: {},
// `config` is the config that was provided to `axiod` for the request
config: {}
}
When using then, you will receive the response as follows:
import axiod from "https://deno.land/x/axiod/mod.ts";
axiod.get("/user/12345").then((response) => {
console.log(response.data);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
console.log(response.config);
});
Interceptors
You can intercept requests or responses before they are handled by then or catch.
PS: Unlike Axios, Request interceptor does not have an error interceptor in Axiod, I don’t really see the need for it
// Add a request interceptor
axid.interceptors.request.use(function (config) {
// Do something before request is sent
return config;
});
// Add a response interceptor
axid.interceptors.response.use(
function (response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response;
},
function (error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
return Promise.reject(error);
}
);
If you need to remove an interceptor later you can.
const myInterceptor = axiod.interceptors.request.use(function () {
/*...*/
});
axiod.interceptors.request.eject(myInterceptor);
You can add interceptors to a custom instance of axiod.
PS: interceptors are NOT inherited from parent instance
const instance = axiod.create();
instance.interceptors.request.use(function () {
/*...*/
});
Check ./__tests__/interceptors.test.ts
for some examples
Multiple Interceptors
Given you add multiple response interceptors and when the response was fulfilled
- then each interceptor is executed
- then they are executed in the order they were added
- then only the last interceptor’s result is returned
- then every interceptor receives the result of it’s predecessor
- and when the fulfillment-interceptor throws
- then the following fulfillment-interceptor is not called
- then the following rejection-interceptor is called
- once caught, another following fulfill-interceptor is called again (just like in a promise chain).
Handling Errors
import axiod from "https://deno.land/x/axiod/mod.ts";
axiod.get("/user/12345").catch((error) => {
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else {
// Something happened in setting up the request that triggered an Error
console.log("Error", error.message);
}
console.log(error.config);
});
Testing
// To test the module just run
deno test --allow-net
License
MIT