Skip to main content

Remote - HTTP Client

Introduction​

Often in our applications, we need to make HTTP requests to external services. This can be done using the _remote resource.

To make HTTP calls internally to other services or internal servers on the computer, or to call a service on the internet, using the Remote resource is essential.

See the documentation for the Remote resource.

We can even send and receive data in JSON and upload or download files.

Initialization​

To make an HTTP call, we must first initialize it. To do this, we will use the init method of _remote and then store the result of this operation in a variable called remote so that we can reference the request:

// Supports sending HTTP Parameters
const simpleRemote = _remote.init();
// Supports sending JSON:
const jsonRemote = _remote.init().asJSON();

Configuration​

We can also define global settings in the application; note this complete example:

...
"remote": {
"example-basic-auth": {
"url": "https://example.com/api/rest/example",
"form": true,
"authorization": {
"username": "admin",
"password": "pass"
},
"data": {
"param1": "value 1...",
"param2": "value 2..."
}
},
"example-auth-token": {
"urlPrefix": "https://example.com/api",
"json": true,
"authorization": "Bearer AUTH-TOKEN-HERE",
"connectTimeout": 5000,
"readTimeout": 60000,
}
},
...

We can configure a URL prefix (urlPrefix) or the full URL (url).

We can define whether the request should by default send data as a form or JSON.

It supports sending basic authorization with username and password or token.

And it is possible to define the default data that will be sent (data), as well as the connection (connectTimeout) and read (readTimeout) timeouts.

How to Use​

To use the Remote resource with the settings defined in the application, simply initialize the resource with the configuration key, for example:

const response = _remote.init("example-basic-auth").post();

And if the prefix URL is defined:

const remote = _remote.init("example-auth-token");
const response = remote.post(
"/services/load-data",
_val.map()
.set("param1", "value 1")
.set("param2", "value 2")
);

HTTP Request​

See how we can start an HTTP request using the Remote resource.

Send JSON​

To send data in JSON format, we need to signal the content type as JSON:

The simple way to do this is by using the asJSON() method, for example:

const remote = _remote.init().asJSON();
remote.setURL("http://example.com/save");
const response = remote.post(
_val.map()
.set("data", "example")
.set("json", true)
);

This way data will be sent as JSON for POST and PUT HTTP methods, but for GET and DELETE data is always sent in the query string because the HTTP protocol does not support a body with data for these methods.

HTTP Methods​

We do not need to explicitly indicate the HTTP method in the request header; for this, our request reference has its own function for each type of HTTP method:

const remote = _remote.init();

remote.setURL("http://example.com");

remote.get(); // for GET calls
remote.post(); // for POST calls
remote.put(); // for PUT calls
remote.delete(); // for DELETE calls

HTTP Header​

To configure HTTP header parameters, we can set the keys with the getHeader() method, for example:

const remote = _remote.init();

remote.getHeader().set("KEY", "VALUE");

Request URL​

To indicate the request URL, simply pass it as the first parameter of the functions for the respective HTTP methods:

const remote = _remote.init().asJSON()

remote.get("http://example.com") // for GET calls
remote.post("http://example.com") // for POST calls
remote.put("http://example.com") // for PUT calls
remote.delete("http://example.com") // for DELETE calls

Adding data to the request​

If you need to add data to the request body, simply pass it as the second parameter of the function for the respective method:

const remote = _remote.init().asJSON();

remote.get("http://example.com", _val.map().set("user_id", 1)); // for GET calls
remote.post("http://example.com", _val.map().set("user_id", 1)); // for POST calls
remote.put("http://example.com", _val.map().set("user_id", 1)); // for PUT calls
remote.delete("http://example.com", _val.map().set("user_id", 1)); // for DELETE calls

Response​

After receiving the response from the request, we can read its data using the json or content method:

const remote = _remote.init().asJSON();

const response = remote.post(
"http://example.com",
_val.map().set("user_id", 1)
);

if (response.ok()) {
_log.info("Response in JSON", response.json());
} else {
_log.info("Failed with status: "+ response.statusCode(), response.content());
}

In the response, with the json() function we get the array or object provided as Values, and with the content() function we get raw text content or the HTML, and the received status code number comes in statusCode(), while ok() returns a boolean value that is true if the status code is within the 200 to 299 range.

Download​

To download files, we use remote with asBinary() to indicate that it should be a binary transfer.

See this complete service example:

server/services/file-download.js
const download = _remote.asBinary()
.get('https://raw.githubusercontent.com/netuno-org/platform/main/docs/images/overview.png');

if (!download.ok()) {
_log.error(`File download status: ${download.statusCode()}\n${download.toString()}`);
_header.status(500);
_out.json({result: false, error: 'file-download-failed'});
_exec.stop();
}

const file = download.file();
_log.info(`Downloaded file name: ${file.name()}`);

file.save(_app.file(`storage/filesystem/server/${file.name()}`));
file.save(_storage.filesystem("server", "other.png").file());
file.save(_os.file("/tmp/download.png"));
_out.json({result: true});

file.save is used 3 times on purpose; to learn more about _app.file, _storage, and _os.file, see the tutorial on File and Folder Manipulation.

Upload​

To send a file structured as a form in multipart format, we use asMultipartFormData().

See the complete example of a service:

server/services/file-upload.js
const file = _app.file("public/images/logo.png");

const upload = _remote.asMultipartFormData()
.post(
'http://localhost:9000/services/file-receiver',
_val.map()
.set("file", file)
);

if (upload.ok()) {
_log.info(`Successfully uploaded: ${file.name()}`);
_out.json({result: true});
} else {
_log.error(`File upload status: ${upload.statusCode()}\n${upload.toString()}`);
_header.status(500);
_out.json({result: false, error: 'file-upload-failed'});
}

Notice that in the example above the file is sent to another service; see how this other service that receives the file can be implemented in Netuno:

server/services/file-receiver.js
const file = _req.getFile("file");
_log.info(`Received file name: ${file.name()}`);
file.save(_storage.filesystem("server", "received.png").file());
_out.json({result: true});

Conclusion​

With the Remote resource we can integrate external APIs, and we can export, import, or synchronize data.

We can even integrate services implemented in other technologies, which allows performing specific operations provided by other languages.