Our HTTP Client exposes a Fetch-like interface with a twist: making url part of the request object. It extends Fetch API to support request and response interceptors and performs response & header serialization.
Here is a simple example how to make POST
HTTP request:
import SwaggerClient from 'swagger-client';
const request = {
url: 'https://httpbin.org/post',
mode: 'cors',
method: 'POST',
body: { data: 3 },
headers: {
'Content-Type': 'application/json',
},
};
SwaggerClient.http(request); // => Promise(Response)
Here is an example how to make POST
HTTP request via Fetch compatible interface.
import SwaggerClient from 'swagger-client';
const url = 'https://httpbin.org/post';
const request = {
url, // notice the url must always be part of Request object
mode: 'cors',
method: 'POST',
body: { data: 3 },
headers: {
'Content-Type': 'application/json',
},
};
SwaggerClient.http(url, request); // => Promise(Response)
If you prefer using object literal to represent Fetch compatible Request here is how you can create a new request.
const request = {
url: 'https://httpbin.org/post',
mode: 'cors',
method: 'POST',
body: { data: 3 },
headers: {
'Content-Type': 'application/json',
},
};
It is also possible to use instance of Fetch compatible Request class either the one that is native to browsers or provided by 3rd party libraries like cross-fetch.
const request = new Request('https://httpbin.org/post', {
mode: 'cors',
method: 'POST',
body: { data: 3 },
headers: {
'Content-Type': 'application/json',
},
});
You can find documentation of all allowed Request
properties in official documentation of Fetch compatible Request.
Additional Request properties are described in the following table.
Type notations are formatted like so:
String=""
means a String type with a default value of""
.String=["a"*, "b", "c", "d"]
means a String type that can bea, b, c, or d
, with the*
indicating thata
is the default value.
Property | Description |
---|---|
query |
Object=null . When provided, HTTP Client serialize it and appends the queryString to Request.url property. |
loadSpec |
Boolean=undefined . This property will be present and set to true when the Request was constructed internally by SwaggerClient to fetch the OAS definition defined by url or when resolving remote JSON References. |
requestInterceptor |
Function=identity . Either synchronous or asynchronous function transformer that accepts Request and should return Request . |
responseInterceptor |
Function=identity . Either synchronous or asynchronous function transformer that accepts Response and should return Response . |
userFetch |
Function=cross-fetch . Custom asynchronous fetch function that accepts two arguments: the url and the Request object and must return a Response object. |
HTTP Client support serialization of additional query parameters provided as a
query
property of a Request
object. When provided, the query parameters
are serialized and appended to Request.url
property as query string.
Basic example:
import SwaggerClient from 'swagger-client';
const request = {
url: 'https://httpbin.org/?one=1&two=1',
query: {
two: {
value: 2
},
three: {
value: 3
}
},
method: 'GET',
};
SwaggerClient.http(request);
// Requested URL: https://httpbin.org/?one=1&two=2&three=3
Advanced example:
import SwaggerClient from 'swagger-client';
const request = {
url: 'https://httpbin.org/',
query: {
anotherOne: ['one', 'two'], // no collection format
evenMore: 'hi', // string, not an array
bar: { // has a collectionFormat
collectionFormat: 'ssv', // supported values: csv, ssv, pipes
value: [1, 2, 3]
}
},
method: 'GET',
};
SwaggerClient.http(request);
// Requested URL: https://httpbin.org/?anotherOne=one,two&evenMore=hi&bar=1%202%203
loadSpec
property signals when the Request was constructed implicitly by SwaggerClient
.
This can happen in only two circumstances.
- When
SwaggerClient
fetches the OAS definition specified byurl
- When
SwaggerClient
resolves the OAS definition by fetching remote JSON References
All other requests will not have this property present, or the property will be set to false
.
This following code snippet:
import SwaggerClient from 'swagger-client';
const requestInterceptor = (request) => {
console.log(request);
return request;
};
SwaggerClient({ url: 'https://petstore.swagger.io/v2/swagger.json', requestInterceptor })
.then(client => client.execute({
operationId: "findPetsByStatus",
parameters: { status: 3 },
requestInterceptor,
}));
Will result in two requests, with only one containing loadSpec
set to true
:
{
url: 'https://petstore.swagger.io/v2/swagger.json',
loadSpec: true,
requestInterceptor: [Function: requestInterceptor],
responseInterceptor: null,
headers: { Accept: 'application/json, application/yaml' },
credentials: 'same-origin'
}
{
url: 'https://petstore.swagger.io/v2/pet/findByStatus?status=3',
credentials: 'same-origin',
headers: {},
requestInterceptor: [Function: requestInterceptor],
method: 'GET'
}
Request interceptor can be configured via requestInterceptor
property in Request
object.
When set, it intercepts the Request
object before the actual HTTP request is made and after
the query serialization
kicked in. This means that intercepted Request
object will
never contain query
property. All other properties will be present though. Request interceptor
can be defined as synchronous (transformer) or asynchronous function (allows other async operations inside).
Transformer usage:
import SwaggerClient from 'swagger-client';
const request = {
url: 'https://httpbin.org/',
method: 'GET',
requestInterceptor: req => {
req.url += '?param=value';
return req;
},
};
SwaggerClient.http(request);
// Requested URL: https://httpbin.org/?param=value
Async operation usage:
import SwaggerClient from 'swagger-client';
const request = {
url: 'https://httpbin.org/',
method: 'GET',
requestInterceptor: async req => {
const { body: { idToken } } = await SwaggerClient.http({ url: 'https://identity.com/idtoken.json' });
req.url += `?idToken=${idToken}`;
return req;
},
};
SwaggerClient.http(request);
// Requested URL: https://httpbin.org/?idToken=2342398423948923
You're not limited to using one Request interceptor function. You can easily
compose pipe
of request interceptors.
import SwaggerClient from 'swagger-client';
const pipeP = (...fns) => args => fns.reduce((arg, fn) => arg.then(fn), Promise.resolve(args))
const interceptor1 = req => {
req.url += '?param1=value1';
return req;
}
const interceptor2 = async req => {
req.url += '¶m2=value2';
return Promise.resolve(req);
};
const request = {
url: 'https://httpbin.org/',
method: 'GET',
requestInterceptor: pipeP(interceptor1, interceptor2),
};
SwaggerClient.http(request);
// Requested URL: https://httpbin.org/?param1=value1¶m2=value2
Note: you can mutate or assign any property of
Request
object as long as your interceptor produces a validRequest
object again.
Although we internally use Fetch Compatible Response, we expose a POJO compatible in shape with Fetch Compatible Response.
Below is a list of Response properties:
Property | Description |
---|---|
ok |
Boolean . A boolean indicating whether the response was successful (status in the range 200–299) or not. |
status |
Number . The status code of the response. (This will be 200 for a success). |
statusText |
String . The status message corresponding to the status code. (e.g., OK for 200). |
url |
String . Request url. |
headers |
Object . The Headers object associated with the response. |
text |
String | Blob . Textual body, or Blob. |
data |
String | Blob . Textual body, or Blob. (Legacy property) |
body |
Object=undefined . JSON object or undefined . |
obj |
Object=undefined . JSON object or undefined . Mirrors body property. (Legacy property) |
Response interceptor can be configured via responseInterceptor
property in Request
object.
When set, it intercepts the Response
object after the actual HTTP request was made.
Response interceptor can be defined as synchronous (transformer) or asynchronous function (allows other async operations inside).
Transformer usage:
import SwaggerClient from 'swagger-client';
const request = {
url: 'https://httpbin.org/',
method: 'GET',
responseInterceptor: res => {
res.arbitraryProp = 'arbitrary value';
return res;
},
};
SwaggerClient.http(request);
/**
* Promise({
* ok: true,
* status: 200,
* statusText: 'OK',
* url: 'https://httpbin.org/',
* headers: {...},
* text: '{"prop":"value"}',
* data: '{"prop":"value"}',
* body: {prop: 'value'},
* obj: {prop: 'value'},
* arbitraryProp: 'arbitrary value',
* })
*/
Async operation usage:
import SwaggerClient from 'swagger-client';
const request = {
url: 'https://httpbin.org/',
method: 'GET',
responseInterceptor: async res => {
const { body: { idToken } } = await SwaggerClient.http({ url: 'https://identity.com/idtoken.json' });
res.idToken = idToken;
return res;
},
};
SwaggerClient.http(request);
/**
* Promise({
* ok: true,
* status: 200,
* statusText: 'OK',
* url: 'https://httpbin.org/',
* headers: {...},
* text: '{"prop":"value"}',
* data: '{"prop":"value"}',
* body: {prop: 'value'},
* obj: {prop: 'value'},
* idToken: '308949304832084320',
* })
*/
You're not limited to using one Response interceptor function. You can easily
compose pipe
of response interceptors.
import SwaggerClient from 'swagger-client';
const pipeP = (...fns) => args => fns.reduce((arg, fn) => arg.then(fn), Promise.resolve(args))
const interceptor1 = res => {
res.prop += 'value1';
return res;
}
const interceptor2 = async res => {
res.prop += '+value2';
return Promise.resolve(res);
};
const request = {
url: 'https://httpbin.org/',
method: 'GET',
responseInterceptor: pipeP(interceptor1, interceptor2),
};
SwaggerClient.http(request);
/**
* Promise({
* ok: true,
* status: 200,
* statusText: 'OK',
* url: 'https://httpbin.org/',
* headers: {...},
* text: '{"prop":"value"}',
* data: '{"prop":"value"}',
* body: {prop: 'value'},
* obj: {prop: 'value'},
* prop: 'value1+value2',
* })
*/
Note: you can mutate or assign any property of
Response
object as long as your interceptor produces a validResponse
object again.
In order to access original Request
in responseInterceptor
you have to declare
responseInterceptor
either as function declaration or function expression.
By doing so, the interceptor will be bound to the original Request
.
import SwaggerClient from 'swagger-client';
const request = {
url: 'https://httpbin.org/',
method: 'GET',
responseInterceptor: function(res) {
const request = this;
console.log(request.url); // https://httpbin.org/
console.log(request.method); // GET
res.arbitraryProp = 'arbitrary value';
return res;
},
};
SwaggerClient.http(request);
Note: this will not work if arrow functions are used to define a
responseInterceptor
.
We detect the response Content-Type
which identifies the payload of the Response
.
If the Content-Type
headers signals that the data can be represented as something we can parse,
we parse the data as and expose the result as Response.body
property.
Supported serialization formats: json
, yaml
Our headers serialization algorithm serializes headers into an object, where mutliple-headers result in an array.
// eg: Cookie: one
// Cookie: two
// = { Cookie: [ "one", "two" ]
HTTP Client promise will reject with a TypeError when a network error is encountered or CORS is misconfigured on the server-side, although this usually means permission issues or similar.
import SwaggerClient from 'swagger-client';
const request = {
url: 'https://httpbin.org/',
method: 'GET',
};
SwaggerClient.http(request); // network problem or CORS misconfiguration
/**
* Rejected promise will be returned.
*
* Promise.<Error>
*/
HTTP Client will reject all responses with ok
field set to false
- status outside of the range 200-299.
This is another distinction from standard Fetch API
.
import SwaggerClient from 'swagger-client';
const request = {
url: 'https://httpbin.org/non-existing-path',
method: 'GET',
};
SwaggerClient.http(request);
/**
* Rejected promise will be returned.
*
* Promise.<Error{
* message: 'NOT FOUND',
* statusCode: 404,
* response: Response // original response object
* }>
*
*/
When there is an error during headers or response serialization, the Response
object will contain one additional property parseError
and body
/obj
properties
will not be present in Response
.
Property | Description |
---|---|
parseError |
Error=undefined . Error produced during headers or response serialization. |
If you defined responseInterceptor
on Request
object, it can theoretically produce an unexpected Error.
When it happens HTTP Client will produce a rejected promise with following signature:
import SwaggerClient from 'swagger-client';
const request = {
url: 'https://httpbin.org/',
method: 'GET',
responseInterceptor: () => { throw new Error('error'); },
};
SwaggerClient.http(request);
/**
* Rejected promise will be returned.
*
* Promise.<Error{
* message: 'OK',
* statusCode: 200,
* responseError: Error // error thrown in responseInterceptor
* }>
*
*/
HTTP client allows you to override the behavior of how it makes actual HTTP Request
by supplying an asynchronous function under the property called userFetch
in Request
object.
Warning: userFetch
function must return instance of Fetch Compatible Response class or
a compatible object of expected shape or behavior. After importing the SwaggerClient
in your current module, the Response
symbol will be available in the current scope.
Using axios library as override.
import SwaggerClient from 'swagger-client';
import axios from 'axios';
const request = {
url: 'https://httpbin.org/',
method: 'GET',
userFetch: async (url, req) => {
const axiosRequest = { ...req, data: req.body };
const axiosResponse = await axios(axiosRequest);
return new Response(axiosResponse.data.data, {
status: response.status,
headers: response.headers,
});
},
};
SwaggerClient.http(request);
Using axios library as override in browser and tracking upload progress.
<html>
<head>
<script src="//unpkg.com/swagger-client"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.js"></script>
<script>
const request = {
url: 'https://httpbin.org/post',
method: 'POST',
body: "data".repeat(1000000),
userFetch: async (url, req) => {
const onUploadProgress = (progressEvent) => {
const completed = Math.round((progressEvent.loaded * 100) / progressEvent.total);
console.log(`${completed}%`);
}
const axiosRequest = { ...req, data: req.body, onUploadProgress };
const axiosResponse = await axios(axiosRequest);
return new Response(axiosResponse.data.data, {
status: response.status,
headers: response.headers,
});
},
};
SwaggerClient.http(request);
</script>
</head>
<body>
check console in browser's dev. tools
</body>
</html>
Cross-Origin Resource Sharing (CORS
) is a mechanism that uses additional HTTP headers
to tell browsers to give a web application running at one origin,
access to selected resources from a different origin. A web application executes a cross-origin HTTP request
when it requests a resource that has a different origin (domain, protocol, or port) from its own.
HTTP Client supports cors via two properties on Request
object.
Property | Description |
---|---|
mode |
String=["cors"*, "no-cors", "same-origin", "navigate"] . Contains the mode of the request. See more in Request.mode documentation. |
credentials |
String=["omit", "same-origin"*, "include"] . Contains the credentials of the request. See more in Request.credentials documentation. |
If you need to activate CORS globally for every request, just enable it by withCredentials
on HTTP Client.
When enabled, it automatically sets credentials="include"
on every request implicitly for you.
import SwaggerClient from 'swagger-client';
SwaggerClient.http.withCredentials = true;
You may cancel requests with AbortController. The AbortController interface represents a controller object that allows you to abort one or more Web requests as and when desired. Using AbortController, you can easily implement request timeouts.
AbortController needs to be introduced in Node.js environment via abort-controller npm package.
const SwaggerClient = require('swagger-client');
const AbortController = require('abort-controller');
const controller = new AbortController();
const { signal } = controller;
const timeout = setTimeout(() => {
controller.abort();
}, 1);
(async () => {
try {
await SwaggerClient.http({ url: 'https://www.google.com', signal });
} catch (error) {
if (error.name === 'AbortError') {
console.error('request was aborted');
}
} finally {
clearTimeout(timeout);
}
})();
AbortController is part of modern Web APIs. No need to install it explicitly.
<html>
<head>
<script src="//unpkg.com/swagger-client"></script>
<script>
const controller = new AbortController();
const { signal } = controller;
const timeout = setTimeout(() => {
controller.abort();
}, 1);
(async () => {
try {
await SwaggerClient.http({ url: 'https://www.google.com', signal });
} catch (error) {
if (error.name === 'AbortError') {
console.error('request was aborted');
}
} finally {
clearTimeout(timeout);
}
})();
</script>
</head>
<body>
check console in browser's dev. tools
</body>
</html>
It's also possible (for convenience) to call HTTP Client from SwaggerClient
instances.
import SwaggerClient from 'swagger-client';
const { client } = new SwaggerClient({ spec: 'http://petstore.swagger.io/v2/swagger.json' });
client.http(request);