12. JavaScript HTTP functions

12.1. Choosing an HTTP library
We have chosen two libraries here:
ECMAScript 6 natively has an HTTP function called [fetch] that is not implemented by [node.js] (as of September 2019). There is a library called [node-fetch] that allows you to use the [fetch] function in Node. This library uses certain APIs specific to [node.js]. [node-fetch] code may therefore not be 100% portable to a non-[node] environment, such as a browser;
There is also a library called [axios] dedicated to HTTP requests that is compatible with both [node.js] and browsers. This is the library we will ultimately use.
We will present the same script written using both libraries to demonstrate that the coding approach is similar.
12.2. Setting up a development environment
12.2.1. Installing the tax calculation server
Ultimately, we will write a web application with the following architecture:

JS: JavaScript
The JavaScript code is client-side:
- from a service providing static pages or fragments;
- a JSON service;
The JavaScript code is therefore a JSON client and, as such, can be organized into layers [UI, business logic, DAO] (UI: User Interface) just like our JSON clients written in PHP.
The server will be the tax calculation server for which we have already written 13 versions. We are going to write a 14th one. We therefore begin by duplicating, in NetBeans, the version 13 folder into the version 14 folder:

- in [6], we modify the [config.json] file of version 14 as follows:
{
"databaseFilename": "Config/database.json",
"rootDirectory": "C:/myprograms/laragon-lite/www/php7/scripts-web/impots/version-14",
"relativeDependencies": [
"/Entities/BaseEntity.php",
"/Entities/Simulation.php",
...
"views": {
"authentication-view.php": [700, 221, 400],
"tax-calculation-view.php": [200, 300, 341, 350, 800],
"simulation-list-view.php": [500, 600]
},
"error-views": "error-views.php"
}
- On line 3, we change the application's root directory;
To access this server, you must start the [Laragon] services.
Once this is done, we can test this new version of the server—which is currently identical to version 13—using [Postman] (see linked article). We can use the collection of requests used to test version 12 of the tax calculation server:

- in [1-4], use the [init-session-700] request to initialize a JSON session;
- in [4-5], replace [version-12] with [version-14] to test version 14 of the project;
- upon execution, we should receive the JSON response [6] from the server;
Version 14 of the server is now operational. We will need to modify it slightly. Let’s review this server’s API:
Action | Role | Execution Context |
init-session | Used to set the type (json, xml, html) of the desired responses | GET request main.php?action=init-session&type=x can be sent at any time |
authenticate-user | Authorizes or denies a user's login | POST request main.php?action=authenticate-user The request must have two posted parameters [user, password] Can only be issued if the session type (json, xml, html) is known |
calculate-tax | Performs a tax calculation simulation | POST request to main.php?action=calculate-tax The request must have three posted parameters [married, children, salary] Can only be issued if the session type (json, xml, html) is known and the user is authenticated |
list-simulations | Request to view the list of simulations performed since the start of the session | GET request main.php?action=list-simulations The request does not accept any other parameters Can only be issued if the session type (json, xml, html) is known and the user is authenticated |
delete-simulation | Deletes a simulation from the list of simulations | GET request main.php?action=list-simulations&number=x The request does not accept any other parameters Can only be issued if the session type (json, xml, html) is known and the user is authenticated |
end-session | Ends the simulation session. | Technically, the old web session is deleted and a new session is created Can only be issued if the session type (json, xml, html) is known and the user is authenticated |
12.2.2. Installation of the JavaScript client HTTP libraries
Initially, we will work with the following architecture:

- In [1], a console script [node.js] makes an HTTP request to the tax calculation JSON server;
- In [4], it receives this response and displays it in the console;
In Example 1, we will use the [node-fetch] and [axios] libraries, and then we will only keep [axios] for the following examples. We will now install these two JavaScript libraries from the [VSCode] terminal:

We will also use the [qs] library, which allows for URL encoding of a string. Recall that this encoding is used to encode the parameters of an HTTP GET or POST request.

12.3. script [fetch-01]
The [fetch-01] script uses the [node-fetch] library to initialize a JSON session with the tax calculation server. Its code is as follows:
'use strict';
// imports
import fetch from 'node-fetch';
import qs from 'qs';
import { sprintf } from 'sprintf-js';
import moment from 'moment';
// Base URL of the tax calculation server
const baseUrl = 'http://localhost/php7/scripts-web/impots/version-14/main.php?';
// Initialize session
async function initSession() {
// HTTP request options [get /main.php?action=init-session&type=json]
const options = {
method: "GET",
timeout: 2000
};
// Execute the HTTP request [get /main.php?action=init-session&type=json]
let startFetch;
try {
// asynchronous request - [fetch] returns a promise
startFetch = moment(Date.now());
const response = await fetch(baseUrl + qs.stringify({
action: 'init-session',
type: 'json'
}), options);
// [response] is the entire HTTP response from the server (HTTP headers + the response itself)
// we display this response to see its structure
console.log(sprintf("Fetch response formatted as JSON,=%j, %s", response, time(startFetch)));
console.log("Fetch response in JavaScript =", response);
// we can access the HTTP headers
console.log("response headers=", response.headers);
// If the response type is application/json, the JSON response from the server is retrieved using the asynchronous function [response.json()]
// In this case, the calling code receives a [Promise] object
// [await] allows you to retrieve the server's [json] response rather than its promise
const startJson = moment(Date.now());
const object = await response.json();
console.log(sprintf("json response=%j, type=%s, %s", object, typeof(object), time(startJson)));
return object;
// if the response is of type text / plain, the server's text response is obtained with [response.text()]
// in this case, the calling code receives a [Promise] object
// [await] allows you to get the server's [text] response rather than its promise
// const text = await response.text();
// console.log("text response=", text);
// return text;
} catch (error) {
// We're here because the server sent an error code [404 Not Found, ...] with an empty body—we log the error to see its structure
// or because the client [fetch] threw an exception (network unreachable, ...)
// we display the error structure
console.log(sprintf("error fetch in json=%j, %s", error, time(startFetch)));
console.log("fetch error in JavaScript=", typeof(error), error);
// we log the received error message
throw error.message;
}
}
// The main function executes the asynchronous function [initSession]
async function main() {
try {
console.log("HTTP request to the server in progress ---------------------------------------------");
const response = await initSession();
console.log("success ---------------------------------------------");
console.log("response=", response, typeof (response))
} catch (error) {
console.log("error ---------------------------------------------");
console.log("error=", error, typeof(error));
}
}
// test
main();
// utility for displaying time and duration
function getTime(start) {
// current time
const now = moment(Date.now());
// time formatting
let result = "time=" + now.format("HH:mm:ss:SSS");
// Do we need to calculate a duration?
if (start) {
const duration = now - start;
const milliseconds = duration % 1000;
const seconds = Math.floor(duration / 1000);
// Format time + duration
result = result + sprintf(", duration= %s seconds and %s milliseconds", seconds, milliseconds);
}
// result
return result;
}
Comments
- JavaScript HTTP functions are asynchronous functions. Here, we’re applying what we learned in the previous section (see link);
- line 24: to wait for the response from the asynchronous function [fetch] to be published on the [node.js] event loop, we use the keyword [await]. We know that this statement must be within code prefixed by the keyword [async] (line 13);
- lines 13–56: we encapsulate the HTTP code within the asynchronous function [initSession];
- lines 59–69: a second asynchronous function [main] is used to call the asynchronous function [initSession] in a blocking manner (async/await);
- line 72: the asynchronous function [main] is called;
- although the entire code resembles synchronous code, these are indeed asynchronous functions that are executed, but in a blocking manner;
- line 19: to initialize a JSON session with the tax calculation server, you must send it the HTTP request [get /main.php?action=init-session&type=json]. This is what the code in lines 24–27 does. The syntax for [fetch] is as follows: [fetch(URL, options)] with:
- [URL]: the URL being queried;
- [options]: an object defining the request options. This is where we define the HTTP headers we want to send to the target server;
- lines 15–18: we define the options for the request we want to make:
- [method]: we want to perform a GET;
- [timeout]: we want the [fetch] client to wait no more than 2 seconds for the server’s response. If this timeout is exceeded, [fetch] will throw an exception;
- line 24: to obtain the URL [/main.php?action=init-session&type=json], we use the [qs] library to get the URL-encoded parameters [action,type] of the GET request. The resulting string is [init-session&type=json], which we could have constructed ourselves. We simply wanted to demonstrate how to obtain a URL-encoded string;
- line 24: the keyword [await] indicates that an asynchronous task is being launched here and that we are waiting for it to publish its response to the [node.js] event loop;
- line 24: in [response], we get a complex object that describes the entire received HTTP response (headers and body);
- lines 30–31: We print the [response] object to see its structure, first as a string and then as a JavaScript object;
- line 33: we display the HTTP headers sent by the server;
- Line 38: We know that the tax calculation server will send a JSON string. This string is encapsulated in the [response] object. We can retrieve it using the [response.json()] method. However, this method is asynchronous. We therefore write [await response.json()] to retrieve the JSON string that will be published on the [node.js] event loop. In fact, it is not the JSON string itself that we obtain, but the JavaScript object represented by it;
- line 39: display of the received JSON string;
- line 40: returns the received JavaScript object;
- line 47: we catch any potential errors from the [fetch] statement. This only throws an exception if the HTTP operation failed and no response was received from the server. If a response was received, even with an HTTP status code other than [200 OK], [fetch] does not throw an exception, and the server response will be available on line 38;
- lines 51–52: the [error] object received by the [catch] clause is displayed, first as a JSON string and then as a JavaScript object;
- line 54: the error message from [fetch] is stored in [error.message];
- lines 59–69: the asynchronous function [main] calls the asynchronous function [initSession] in a blocking manner (await on line 62);
- line 72: the asynchronous function [main] is launched, and the main script code is then complete. The overall script will finish when the launched asynchronous tasks have published their results to the event loop;
The results of the execution are as follows:
Case 1: the Laragon server is not running
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\fetch-01.js"
HTTP request to the server in progress ---------------------------------------------
Fetch error in JSON: {"message":"network timeout at: http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json","type":"request-timeout"}, time=10:08:48:180, duration= 2 seconds and 62 milliseconds
fetch error in JavaScript = object { FetchError: network timeout at: http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json
at Timeout.<anonymous> (c:\Data\st-2019\dev\es6\javascript\node_modules\node-fetch\lib\index.js:1448:13)
at ontimeout (timers.js:436:11)
at tryOnTimeout (timers.js:300:5)
at listOnTimeout (timers.js:263:5)
at Timer.processTimers (timers.js:223:10)
message:
'network timeout at: http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json',
type: 'request-timeout' }
error ---------------------------------------------
error= network timeout at: http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json string
[Done] exited with code=0 in 2.804 seconds
Comments
- line 3: the HTTP request fails after 2 seconds and 62 milliseconds due to the 2-second timeout imposed on the HTTP request;
- lines 4–9: the JavaScript [error] object intercepted by the [catch(error)] clause. This object has two properties:
- [FetchError]: line 4;
- [message]: lines 10–12;
- line 14: the error message received by the asynchronous function [main];
Case 2: The Laragon server is running
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\fetch-01.js"
HTTP request to the server in progress ---------------------------------------------
Fetch response formatted in JSON,={"size":0,"timeout":2000}, time=10:13:50:814, duration= 0 seconds and 375 milliseconds
Fetch response in JavaScript = Response {
size: 0,
timeout: 2000,
[Symbol(Body internals)]:
{ body:
PassThrough {
_readableState: [ReadableState],
readable: true,
domain: null,
_events: [Object],
_eventsCount: 2,
_maxListeners: undefined,
_writableState: [WritableState],
writable: false,
allowHalfOpen: true,
_transformState: [Object] },
disturbed: false,
error: null },
[Symbol(Response internals)]:
{ url:
'http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json',
status: 200,
statusText: 'OK',
headers: Headers { [Symbol(map)]: [Object] },
counter: 0 } }
response headers = Headers {
[Symbol(map)]:
[Object: null prototype] {
date: [ 'Sat, 14 Sep 2019 08:13:50 GMT' ],
server: [ 'Apache/2.4.35 (Win64) OpenSSL/1.1.0i PHP/7.2.11' ],
'x-powered-by': [ 'PHP/7.2.11' ],
'cache-control': [ 'max-age=0, private, must-revalidate, no-cache, private' ],
'set-cookie': [ 'PHPSESSID=99q2iinusmhl55fa600aie2mmu; path=/' ],
'content-length': [ '86' ],
connection: [ 'close' ],
'content-type': [ 'application/json' ] } }
response json={"action":"init-session","status":700,"response":"session started with type [json]"}, type=object, time=10:13:50:825, duration= 0 seconds and 1 milliseconds
success ---------------------------------------------
response = { action: 'init-session',
'status': 700,
'response': 'session started with type [json]' } object
[Done] exited with code=0 in 1.022 seconds
Comments
- line 3: [fetch] receives the server response after 375 ms;
- lines 4–39: the structure of the JavaScript object [response] encapsulating the server’s response. Among its properties, some may be of interest to us:
- [status] (line 25): HTTP status code of the server's response;
- [statusText] (line 26): text associated with this code;
- [headers] (line 27): the HTTP headers of the server’s response;
- [body] (line 8): represents the document sent by the server. The [fetch] statement provides methods to work with it;
- lines 29–39: the HTTP headers of the server’s response;
- line 40: the asynchronous function [response.json()] returned its response after 1 millisecond;
- lines 42–44: the JavaScript object received by the asynchronous function [main];
Case 3: The Laragon server is running, but an incorrect command is sent to it:

- above, line 26, an incorrect session type is passed to the server;
The execution results are as follows:
HTTP request to the server in progress ---------------------------------------------
Fetch response formatted as JSON,={"size":0,"timeout":2000}, time=10:27:54:114, duration= 0 seconds and 136 milliseconds
Fetch response in JavaScript = Response {
size: 0,
timeout: 2000,
[Symbol(Body internals)]:
{ body:
PassThrough {
_readableState: [ReadableState],
readable: true,
domain: null,
_events: [Object],
_eventsCount: 2,
_maxListeners: undefined,
_writableState: [WritableState],
writable: false,
allowHalfOpen: true,
_transformState: [Object] },
disturbed: false,
error: null },
[Symbol(Response internals)]:
{ url:
'http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=x',
status: 400,
statusText: 'Bad Request',
headers: Headers { [Symbol(map)]: [Object] },
counter: 0 } }
response headers = Headers {
[Symbol(map)]:
[Object: null prototype] {
date: [ 'Sat, 14 Sep 2019 08:27:54 GMT' ],
server: [ 'Apache/2.4.35 (Win64) OpenSSL/1.1.0i PHP/7.2.11' ],
'x-powered-by': [ 'PHP/7.2.11' ],
'cache-control': [ 'max-age=0, private, must-revalidate, no-cache, private' ],
'set-cookie': [ 'PHPSESSID=5ku9gfok81ikj98hia0meeum57; path=/' ],
'content-length': [ '79' ],
connection: [ 'close' ],
'content-type': [ 'application/json' ] } }
response json={"action":"init-session","status":703,"response":"invalid type=[x] parameter"}, type=object, time=10:27:54:127, duration= 0 seconds and 2 milliseconds
success ---------------------------------------------
response = { action: 'init-session',
'status': 703,
'response': 'invalid type=[x] parameter' } object
[Done] exited with code=0 in 0.712 seconds
- The server response is received on line 2;
- line 24: we can see that the HTTP status code of the server response is 400, an error code. However, [fetch] did not throw an exception. As long as [fetch] receives a response from the server, it processes it and does not throw an exception;
- lines 41–43: the response obtained by the asynchronous function [main];
12.4. script [fetch-02]
The following script reuses the [fetch-01] script while removing all unnecessary details:
'use strict';
// imports
import fetch from 'node-fetch';
import qs from 'qs';
// Base URL of the tax calculation server
const baseUrl = 'http://localhost/php7/scripts-web/impots/version-14/main.php?';
// Initialize session
async function initSession() {
// HTTP request options [get /main.php?action=init-session&type=json]
const options = {
method: "GET",
timeout: 2000
};
// Execute the HTTP request [get /main.php?action=init-session&type=json]
const response = await fetch(baseUrl + qs.stringify({
action: 'init-session',
type: 'json'
}), options);
// Result received in JSON
return await response.json();
}
// The main function executes the asynchronous function [initSession]
async function main() {
try {
console.log("HTTP request to the server in progress ---------------------------------------------");
const response = await initSession();
console.log("success ---------------------------------------------");
console.log("response=", response)
} catch (error) {
console.log("error ---------------------------------------------");
console.log("error=", error.message);
}
}
// test
main();
Results of a normal execution:
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\fetch-02.js"
HTTP request to the server in progress ---------------------------------------------
success ---------------------------------------------
response= { action: 'init-session',
'status': 700,
'response': 'session started with type [json]' }
[Done] exited with code=0 in 0.56 seconds
Results of an execution with an exception (stopping the Laragon server):
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\fetch-02.js"
HTTP request to the server in progress ---------------------------------------------
error ---------------------------------------------
error= network timeout at: http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json
[Done] exited with code=0 in 2.701 seconds
12.5. script [axios-01]
Here we revisit the [fetch-01] script, which we rewrite using the [axios] library. As a reminder, our interest in this library lies in its portability between the [node.js] environment and common browsers. This allows:
- In Phase 1, test our scripts in a [Node.js] environment;
- in phase 2, to port them to a browser;
The [axios-01] script follows the structure of the [fetch-01] script:
'use strict';
import axios from 'axios';
// default axios configuration
axios.defaults.timeout = 2000;
axios.defaults.baseURL = 'http://localhost/php7/scripts-web/impots/version-14';
// initialize session
async function initSession(axios) {
// HTTP request options [get /main.php?action=init-session&type=json]
const options = {
method: "GET",
// URL parameters
params: {
action: 'init-session',
type: 'json'
}
};
// Execute the HTTP request [get /main.php?action=init-session&type=json]
try {
// asynchronous request
const response = await axios.request('main.php', options);
// response is the entire HTTP response from the server (HTTP headers + the response body)
// display this response to see its structure
console.log("axios response=", response);
// The server's response is in [response.data]
return response.data;
} catch (error) {
// we're here because the server sent an error code [404 Not Found, 500 Internal Server Error, ...]
// the [error] parameter is an exception instance—it can take various forms
// we log it to see its structure
console.log("axios error=", typeof (error), error);
if (error.response) {
// The server reported an error in the HTTP status, but it also sent a response
// so this is found in [error.response.data]
// we know that the server sends JSON responses with the structure {action, status, response}
// and that in case of an error, the error message is in [response]
return error.response.data;
} else {
// we throw the error
throw error;
}
}
}
// The main function executes the asynchronous function [initSession]
async function main() {
try {
console.log("HTTP request to the server in progress ---------------------------------------------");
const response = await initSession(axios);
console.log("success ---------------------------------------------");
console.log("response=", response, typeof (response))
} catch (error) {
console.log("error ---------------------------------------------");
console.log("error=", error.message);
}
}
// test
main();
Comments
- line 2: we import the [axios] library;
- lines 5-6: default configuration for HTTP requests. The [axios.defaults] options apply to all HTTP requests sent by the [axios] object without needing to be specified for each new request;
- line 5: 2-second timeout for all requests;
- line 6: all URLs will be relative to the base URL;
- line 9: the asynchronous [initSession] function;
- lines 11–18: the options for the HTTP request to be sent (in addition to the default options already defined in lines 5–6);
- lines 14–17: the URL parameters [action=init-session&type=json]. The [params] object will be automatically converted to a URL-encoded string;
- line 22: blocking call to the asynchronous function [axios.request]. The first parameter is the target URL constructed as [main.php] appended to the base URL defined on line 6. The second parameter is the [options] object from lines 11–18;
- line 25: [response] is a JavaScript object encapsulating the entire HTTP response from the server (HTTP headers + response body). We display it to see its JavaScript structure;
- line 27: if the server sent a document, then it is found in [response.data]. Here we know that the server sends a JSON response accompanied by the HTTP header [Content-type: application/json]. The presence of this header causes [axios] to automatically deserialize [response.data] into a JavaScript object;
- line 28: the [axios] function can throw an exception. This is where [axios] differs from [fetch]. An exception is thrown in the following cases:
- the HTTP request could not be sent (client-side error);
- the server sent an HTTP error code (400, 404, 500, etc.) (this behavior is actually configurable). Note that if this HTTP status code is accompanied by a response, [fetch] did not throw an exception, whereas [axios] does. However, if the HTTP error code is accompanied by a document, it is placed in [error.response];
- line 32: we display the JavaScript structure of the [error] object;
- lines 33–38: if the [error] object contains a [response] object, then this response is returned to the calling code;
- lines 39–42: in all other cases, the [error] object is passed back to the calling code;
- lines 47–57: the asynchronous function [main];
- line 50: blocking call to the asynchronous function [initSession]. The JSON response from the server is retrieved as a JavaScript object;
- lines 53–56: handling any errors. The error message is in [error.message];
The results of the execution are as follows:
Case 1: The Laragon server is not running
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\axios-01.js"
HTTP request to the server in progress ---------------------------------------------
axios error= object { Error: timeout of 2000ms exceeded
at createError (c:\Data\st-2019\dev\es6\javascript\node_modules\axios\lib\core\createError.js:16:15)
at Timeout.handleRequestTimeout (c:\Data\st-2019\dev\es6\javascript\node_modules\axios\lib\adapters\http.js:252:16)
at ontimeout (timers.js:436:11)
at tryOnTimeout (timers.js:300:5)
at listOnTimeout (timers.js:263:5)
at Timer.processTimers (timers.js:223:10)
config:
{ url:
'http://localhost/php7/scripts-web/impots/version-14/main.php',
method: 'get',
params: { action: 'init-session', type: 'json' },
headers:
{ Accept: 'application/json, text/plain, */*',
'User-Agent': 'axios/0.19.0' },
baseURL: 'http://localhost/php7/scripts-web/impots/version-14',
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 2000,
adapter: [Function: httpAdapter],
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
data: undefined },
code: 'ECONNABORTED',
request:
Writable {
_writableState:
WritableState {
objectMode: false,
highWaterMark: 16384,
finalCalled: false,
needDrain: false,
ending: false,
ended: false,
finished: false,
destroyed: false,
decodeStrings: true,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: true,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: null,
writelen: 0,
bufferedRequest: null,
lastBufferedRequest: null,
pendingcb: 0,
prefinished: false,
errorEmitted: false,
emitClose: true,
bufferedRequestCount: 0,
corkedRequestsFree: [Object] },
writable: true,
domain: null,
_events:
[Object: null prototype] {
response: [Function: handleResponse],
error: [Function: handleRequestError] },
_eventsCount: 2,
_maxListeners: undefined,
_options:
{ protocol: 'http:',
maxRedirects: 21,
maxBodyLength: 10485760,
path:
'/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json',
method: 'GET',
headers: [Object],
agent: undefined,
auth: undefined,
hostname: 'localhost',
port: null,
nativeProtocols: [Object],
pathname: '/php7/scripts-web/impots/version-14/main.php',
search: '?action=init-session&type=json' },
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 0,
_requestBodyBuffers: [],
_onNativeResponse: [Function],
_currentRequest:
ClientRequest {
domain: null,
_events: [Object],
_eventsCount: 6,
_maxListeners: undefined,
output: [],
outputEncodings: [],
outputCallbacks: [],
outputSize: 0,
writable: true,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: [Socket],
connection: [Socket],
_header:
'GET /php7/scripts-web/impots/version-14/main.php?action=init-session&type=json HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nUser-Agent: axios/0.19.0\r\nHost: localhost\r\nConnection: close\r\n\r\n',
_onPendingData: [Function: noopPendingOutput],
agent: [Agent],
socketPath: undefined,
timeout: undefined,
method: 'GET',
path:
'/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json',
_ended: false,
res: null,
aborted: 1568528450762,
timeoutCb: null,
upgradeOrConnect: false,
parser: [HTTPParser],
maxHeadersCount: null,
_redirectable: [Circular],
[Symbol(isCorked)]: false,
[Symbol(outHeadersKey)]: [Object] },
_currentUrl:
'http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json' },
response: undefined,
isAxiosError: true,
toJSON: [Function] }
error ---------------------------------------------
error= 2000ms timeout exceeded
[Done] exited with code=0 in 2.784 seconds
Comments
- lines 33-136: the [error] object contains a lot of information;
- [Error], lines 3-9: a description of the error that occurred;
- [config], lines 10–27: the configuration of the HTTP request that led to this error;
- [config.url], lines 11–12: the target URL;
- [config.method], line 13: request method;
- [config.params], line 14: the URL parameters;
- [config.headers], lines 16–17: the HTTP headers of the request;
- [config.baseURL], line 18: the base URL of the target URL;
- [config.timeout], line 21: the request timeout;
- [code], line 28: an error code;
- [request], lines 29–133: a detailed description of the HTTP request. Note that most properties are prefixed with an underscore (_), indicating that they are internal properties of the [request] object not intended to be used directly by the developer;
- [response], line 134: the server response, which is empty here;
- line 138: the error message displayed by the [main] function;
Case 2: The Laragon server is running
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\axios-01.js"
HTTP request to the server in progress ---------------------------------------------
Axios response: { status: 200,
statusText: 'OK',
headers:
{ date: 'Sun, 15 Sep 2019 07:09:26 GMT',
server: 'Apache/2.4.35 (Win64) OpenSSL/1.1.0i PHP/7.2.11',
'x-powered-by': 'PHP/7.2.11',
'cache-control': 'max-age=0, private, must-revalidate, no-cache, private',
'set-cookie': [ 'PHPSESSID=uas6lugtblstktcifpd8e5irm6; path=/' ],
'content-length': '86',
connection: 'close',
'content-type': 'application/json' },
config:
{ url:
'http://localhost/php7/scripts-web/impots/version-14/main.php',
method: 'get',
params: { action: 'init-session', type: 'json' },
headers:
{ Accept: 'application/json, text/plain, */*',
'User-Agent': 'axios/0.19.0' },
baseURL: 'http://localhost/php7/scripts-web/impots/version-14',
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 2000,
adapter: [Function: httpAdapter],
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
data: undefined },
request:
ClientRequest {
domain: null,
_events:
[Object: null prototype] {
socket: [Function],
abort: [Function],
aborted: [Function],
error: [Function],
timeout: [Function],
prefinish: [Function: requestOnPrefinish] },
_eventsCount: 6,
_maxListeners: undefined,
output: [],
outputEncodings: [],
outputCallbacks: [],
outputSize: 0,
writable: true,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket:
Socket {
connecting: false,
_hadError: false,
_handle: [TCP],
_parent: null,
_host: 'localhost',
_readableState: [ReadableState],
readable: true,
domain: null,
_events: [Object],
_eventsCount: 7,
_maxListeners: undefined,
_writableState: [WritableState],
writable: false,
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
parser: null,
_httpMessage: [Circular],
[Symbol(asyncId)]: 6,
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0 },
connection:
Socket {
connecting: false,
_hadError: false,
_handle: [TCP],
_parent: null,
_host: 'localhost',
_readableState: [ReadableState],
readable: true,
domain: null,
_events: [Object],
_eventsCount: 7,
_maxListeners: undefined,
_writableState: [WritableState],
writable: false,
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
parser: null,
_httpMessage: [Circular],
[Symbol(asyncId)]: 6,
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0 },
_header:
'GET /php7/scripts-web/impots/version-14/main.php?action=init-session&type=json HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nUser-Agent: axios/0.19.0\r\nHost: localhost\r\nConnection: close\r\n\r\n',
_onPendingData: [Function: noopPendingOutput],
agent:
Agent {
domain: null,
_events: [Object],
_eventsCount: 1,
_maxListeners: undefined,
defaultPort: 80,
protocol: 'http:',
options: [Object],
requests: {},
sockets: [Object],
freeSockets: {},
keepAliveMsecs: 1000,
keepAlive: false,
maxSockets: Infinity,
maxFreeSockets: 256 },
socketPath: undefined,
timeout: undefined,
method: 'GET',
path:
'/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json',
_ended: true,
res:
IncomingMessage {
_readableState: [ReadableState],
readable: false,
domain: null,
_events: [Object],
_eventsCount: 3,
_maxListeners: undefined,
socket: [Socket],
connection: [Socket],
httpVersionMajor: 1,
httpVersionMinor: 0,
httpVersion: '1.0',
complete: true,
headers: [Object],
rawHeaders: [Array],
trailers: {},
rawTrailers: [],
aborted: false,
upgrade: false,
url: '',
method: null,
statusCode: 200,
statusMessage: 'OK',
client: [Socket],
_consuming: false,
_dumped: false,
req: [Circular],
responseUrl:
'http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json',
redirects: [] },
aborted: undefined,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
_redirectable:
Writable {
_writableState: [WritableState],
writable: true,
domain: null,
_events: [Object],
_eventsCount: 2,
_maxListeners: undefined,
_options: [Object],
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 0,
_requestBodyBuffers: [],
_onNativeResponse: [Function],
_currentRequest: [Circular],
_currentUrl:
'http://localhost/php7/scripts-web/impots/version-14/main.php?action=init-session&type=json' },
[Symbol(isCorked)]: false,
[Symbol(outHeadersKey)]:
[Object: null prototype] { accept: [Array], 'user-agent': [Array], host: [Array] } },
data:
{ action: 'init-session',
'status': 700,
'response': 'session started with type [json]' } }
success ---------------------------------------------
response = { action: 'init-session',
'status': 700,
'response': 'session started with type [json]' } object
[Done] exited with code=0 in 1.115 seconds
Comments
- lines 3–203: the JavaScript object [response] that encapsulates the server’s HTTP response;
- line 3, [status]: the HTTP status code of the response;
- line 4, [statusText]: the text associated with the previous HTTP status code;
- lines 5-13, [headers]: the HTTP headers of the response:
- line 10, [Set-Cookie]: the session cookie;
- line 13, [Content-Type]: the type of document sent by the server;
- lines 14–31, [config]: the configuration of the HTTP request sent;
- lines 32–199, [request]: the JavaScript object detailing the HTTP request sent;
- lines 200–203, [request.data]: the JavaScript object encapsulating the server’s JSON response;
- lines 205–207: the response retrieved by the asynchronous [main] function;
Case 3: An incorrect [init-session] request is sent to the Laragon server;

The execution results are as follows:
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\axios-01.js"
HTTP request to the server in progress ---------------------------------------------
axios error= object { Error: Request failed with status code 400
...
config:
{ url:
'http://localhost/php7/scripts-web/impots/version-14/main.php',
...
data: undefined },
request:
...
[Symbol(outHeadersKey)]:
[Object: null prototype] { accept: [Array], 'user-agent': [Array], host: [Array] } },
response:
{ status: 400,
statusText: 'Bad Request',
headers:
{ date: 'Sun, 15 Sep 2019 07:25:58 GMT',
server: 'Apache/2.4.35 (Win64) OpenSSL/1.1.0i PHP/7.2.11',
'x-powered-by': 'PHP/7.2.11',
'cache-control': 'max-age=0, private, must-revalidate, no-cache, private',
'set-cookie': [Array],
'content-length': '79',
connection: 'close',
'content-type': 'application/json' },
config:
{ url:
'http://localhost/php7/scripts-web/impots/version-14/main.php',
...
data: undefined },
request:
...
[Symbol(outHeadersKey)]: [Object] },
data:
{ action: 'init-session',
'status': 703,
'response': 'invalid type=[x] parameter' } },
isAxiosError: true,
toJSON: [Function] }
success ---------------------------------------------
response = { action: 'init-session',
'status': 703,
'response': 'invalid type=[x] parameter' } object
[Done] exited with code=0 in 0.69 seconds
Because the server responded with an HTTP 400 code (line 15), [axios] threw an exception (again, this behavior is configurable). Although [axios] threw an exception, we still get the HTTP response from the server in [error.response] (line 14) and the sent JSON document in [error.response.data] (line 34). Lines 41–43: the [main] function correctly retrieves the JSON response from the server.
12.6. script [axios-02]
Now that we have detailed the objects handled by the [axios] library during an HTTP request, we can rewrite the [axios-01] script as follows:
'use strict';
import axios from 'axios';
// default axios configuration
axios.defaults.timeout = 2000;
axios.defaults.baseURL = 'http://localhost/php7/scripts-web/impots/version-14';
// initialize session
async function initSession(axios) {
// HTTP request options [get /main.php?action=init-session&type=json]
const options = {
method: "GET",
// URL parameters
params: {
action: 'init-session',
type: 'json'
}
};
try {
// Execute the HTTP request [get /main.php?action=init-session&type=json]
const response = await axios.request('main.php', options);
// the server response is in [response.data]
return response.data;
} catch (error) {
// server response
if (error.response) {
// The JSON response is in [error.response.data]
return error.response.data;
} else {
// throw the error
throw error;
}
}
}
// The main function executes the asynchronous function [initSession]
async function main() {
try {
console.log("HTTP request to the server in progress ---------------------------------------------");
const response = await initSession(axios);
console.log("success ---------------------------------------------");
console.log("response=", response, typeof (response))
} catch (error) {
console.log("error ---------------------------------------------");
console.log("error=", error.message);
}
}
// test
main();
12.7. script [axios-03]
The [axios-03] script follows the same methodology as the [axios-02] script. This time, we add the [authenticate-user] HTTP request to the server, which is sent using a POST request:
'use strict';
import axios from 'axios';
import qs from 'qs'
// axios configuration
axios.defaults.timeout = 2000;
axios.defaults.baseURL = 'http://localhost/php7/scripts-web/impots/version-14';
// initialize session
async function initSession(axios) {
// HTTP request options [get /main.php?action=init-session&type=json]
const options = {
method: "GET",
// URL parameters
params: {
action: 'init-session',
type: 'json'
}
};
try {
// Execute the HTTP request [get /main.php?action=init-session&type=json]
const response = await axios.request('main.php', options);
// the server response is in [response.data]
return response.data;
} catch (error) {
// server response
if (error.response) {
// The JSON response is in [error.response.data]
return error.response.data;
} else {
// Re-throw the error
throw error;
}
}
}
async function authenticateUser(axios, user, password) {
// HTTP request options [POST /main.php?action=authenticate-user]
const options = {
method: "POST",
headers: {
'Content-type': 'application/x-www-form-urlencoded',
},
// POST body
data: qs.stringify({
user: user,
password: password
}),
// URL parameters
params: {
action: 'authenticate-user'
}
};
try {
// Execute the HTTP request [post /main.php?action=authenticate-user]
const response = await axios.request('main.php', options);
// the server response is in [response.data]
return response.data;
} catch (error) {
// server response
if (error.response) {
// The JSON response is in [error.response.data]
return error.response.data;
} else {
// throw the error
throw error;
}
}
}
// The main function executes the asynchronous functions one by one
async function main() {
try {
// init-session
console.log("init-session action in progress ---------------------------------------------");
const response1 = await initSession(axios);
console.log("success ---------------------------------------------");
console.log("response=", response1);
// authenticate-user
console.log("authenticate-user action in progress ---------------------------------------------");
const response2 = await authenticateUser(axios, 'admin', 'admin');
console.log("success ---------------------------------------------");
console.log("response=", response2)
} catch (error) {
console.log("error ---------------------------------------------");
console.log("error=", error);
}
}
// test
main();
Comments
- lines 38-70: the asynchronous function [authenticateUser];
- line 39: the request must be made [POST /main.php?action=authenticate-user];
- lines 40–54: the HTTP request options;
- line 41: this is a POST request;
- lines 42–44: the POST parameters will be URL-encoded in a document that the client sends with its request;
- lines 46–49: the [data] property must contain the URL-encoded POST string. To do this, we use the [qs] library imported on line 3;
- lines 55–69: to execute the request, we use the same code as in the [initSession] method;
- lines 73–89: the [asynchrone] method successively calls the [initSession] and [authentifierUtilisateur] methods in a blocking manner, lines 77 and 82;
- line 82: the pair (admin, admin) is used as the login credentials. We know that they are recognized by the server;
The execution results are as follows:
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\axios-03.js"
init-session action in progress ---------------------------------------------
success ---------------------------------------------
response= { action: 'init-session',
'status': 700,
'response': 'session started with type [json]' }
authenticate-user action in progress ---------------------------------------------
success ---------------------------------------------
response = { action: 'authenticate-user',
'status': 103,
'response':
[ 'No session currently active. Start with action [init-session]' ] }
[Done] exited with code=0 in 0.834 seconds
- lines 9-12: user authentication fails: the server did not retain the fact that a JSON session had been initiated. This is because the session cookie sent in response to the first [init-session] request was not sent back;
12.8. script [axios-04]
The [axios-04] script introduces two improvements to the [axios-03] script:
- it handles the session cookie;
- it factors into a [getRemoteData] function what is common to the [initSession] and [authenticateUser] functions;
'use strict';
import axios from 'axios';
import qs from 'qs'
// Axios configuration
axios.defaults.timeout = 2000;
axios.defaults.baseURL = 'http://localhost/php7/scripts-web/impots/version-14';
// session cookie
const sessionCookieName = "PHPSESSID";
let sessionCookie = '';
// initialize session
async function initSession(axios) {
// HTTP request options [get /main.php?action=init-session&type=json]
const options = {
method: "GET",
// URL parameters
params: {
action: 'init-session',
type: 'json'
}
};
// Execute the HTTP request
return await getRemoteData(axios, options);
}
async function authenticateUser(axios, user, password) {
// HTTP request options [post /main.php?action=authenticate-user]
const options = {
method: "POST",
headers: {
'Content-type': 'application/x-www-form-urlencoded',
},
// POST body
data: qs.stringify({
user: user,
password: password
}),
// URL parameters
params: {
action: 'authenticate-user'
}
};
// execute the HTTP request
return await getRemoteData(axios, options);
}
async function getRemoteData(axios, options) {
// for the session cookie
if (!options.headers) {
options.headers = {};
}
options.headers.Cookie = sessionCookie;
// Execute the HTTP request
let response;
try {
// asynchronous request
response = await axios.request('main.php', options);
} catch (error) {
// The [error] parameter is an exception instance—it can take various forms
if (error.response) {
// the server's response is in [error.response]
response = error.response;
} else {
// we re-throw the error
throw error;
}
}
// response is the entire HTTP response from the server (HTTP headers + the response itself)
// retrieve the session cookie if it exists
const setCookie = response.headers['set-cookie'];
if (setCookie) {
// setCookie is an array
// we search for the session cookie in this array
let found = false;
let i = 0;
while (!found && i < setCookie.length) {
// search for the session cookie
const results = RegExp('^(' + sessionCookieName + '.+?);').exec(setCookie[i]);
if (results) {
// store the session cookie
// eslint-disable-next-line require-atomic-updates
sessionCookie = results[i];
// found
found = true;
} else {
// next element
i++;
}
}
}
// The server's response is in [response.data]
return response.data;
}
// The main function executes the asynchronous functions one by one
async function main() {
try {
// init-session
console.log("init-session action in progress ---------------------------------------------");
const response1 = await initSession(axios);
console.log("success ---------------------------------------------");
console.log("response=", response1);
// authenticate-user
console.log("authenticate-user action in progress ---------------------------------------------");
const response2 = await authenticateUser(axios, 'admin', 'admin');
console.log("success ---------------------------------------------");
console.log("response=", response2)
} catch (error) {
console.log("error ---------------------------------------------");
console.log("error=", error.message);
}
}
// test
main();
Comments
- lines 14–26: the [initSession] function. It now simply prepares the HTTP request to be sent to the server but does not execute it. It delegates this task to the [getRemoteDate] method in lines 49–95;
- lines 28–47: the [authentifierUtilisateur] function follows the same procedure;
- line 49: the [getRemoteData] function receives the two pieces of information it needs to execute an HTTP request:
- [axios], the object responsible for sending the request and receiving the response;
- [options], the configuration options for the request to be sent to the server;
- line 59: executing the request and waiting for its JSON response;
- lines 60–68: handling any exceptions;
- line 64: retrieve the response, which may be encapsulated in the error object;
- line 67: if the server threw an exception without including the server response, then the received error is propagated to the calling code;
- The [getRemoteData] function manages the session cookie:
- it stores it in the [sessionCookie] variable (line 11) when it receives it for the first time;
- it then returns it with each new HTTP request;
- lines 72–92: [getRemoteData] analyzes each server response to determine if it sent the [Set-Cookie] HTTP header. We know that the server sends a session cookie named [PHPSESSID] (line 10). This is therefore the cookie we are looking for (line 10);
- line 72: we retrieve the [Set-Cookie] HTTP headers if they exist (case is not sensitive). There may in fact be multiple [Set-Cookie] headers, so we retrieve an array;
- line 73: if an array of cookies has been retrieved;
- lines 78–90: we search for the session cookie among all the cookies in the array;
- line 80: the relational expression used to search for the session cookie in cookie #i;
- line 81: if the comparison returned results;
- line 84: we have in results[1], the first parenthesis of the relational expression pattern, i.e., (PHPSESSID=xxxx) up to the closing parenthesis (not included) that ends the session cookie;
- lines 50–54: with each request, the session cookie is included in the request’s HTTP headers. The first time, this cookie is empty and will therefore be ignored by the server;
The execution results are as follows:
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\http\axios-04.js"
init-session action in progress ---------------------------------------------
success ---------------------------------------------
response= { action: 'init-session',
'status': 700,
'response': 'session started with type [json]' }
User authentication in progress ---------------------------------------------
success ---------------------------------------------
response = { action: 'authenticate-user',
'status': 200,
'response': 'Authentication successful [admin, admin]' }
[Done] exited with code=0 in 0.982 seconds