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:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
// Supports sending HTTP Parameters
const simpleRemote = _remote.init();
// Supports sending JSON:
const jsonRemote = _remote.init().asJSON();
// Supports sending HTTP Parameters
const simpleRemote = _remote.init();
// Supports sending JSON:
const jsonRemote = _remote.init().asJSON();
# Supports sending HTTP Parameters
simpleRemote = _remote.init()
# Supports sending JSON:
jsonRemote = _remote.init().asJSON()
# Supports sending HTTP Parameters
simpleRemote = _remote.init()
# Supports sending JSON:
jsonRemote = _remote.init().asJSON()
// Supports sending HTTP Parameters
val simpleRemote = _remote.init()
// Supports sending JSON:
val jsonRemote = _remote.init().asJSON()
// Supports sending HTTP Parameters
final simpleRemote = _remote.init()
// Supports sending JSON:
final 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:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const response = _remote.init("example-basic-auth").post();
const response = _remote.init("example-basic-auth").post();
response = _remote.init("example-basic-auth").post()
response = _remote.init("example-basic-auth").post()
val response = _remote.init("example-basic-auth").post()
final response = _remote.init("example-basic-auth").post()
And if the prefix URL is defined:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const remote = _remote.init("example-auth-token");
const response = remote.post(
"/services/load-data",
_val.map()
.set("param1", "value 1")
.set("param2", "value 2")
);
const remote = _remote.init("example-auth-token");
const response = remote.post(
"/services/load-data",
_val.map()
.set("param1", "value 1")
.set("param2", "value 2")
);
remote = _remote.init("example-auth-token")
response = remote.post(
"/services/load-data",
_val.map()
.set("param1", "value 1")
.set("param2", "value 2")
)
remote = _remote.init("example-auth-token")
response = remote.post(
"/services/load-data",
_val.map()
.set("param1", "value 1")
.set("param2", "value 2")
)
val remote = _remote.init("example-auth-token")
val response = remote.post(
"/services/load-data",
_val.map()
.set("param1", "value 1")
.set("param2", "value 2")
)
final remote = _remote.init("example-auth-token")
final 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:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const remote = _remote.init().asJSON();
remote.setURL("http://example.com/save");
const response = remote.post(
_val.map()
.set("data", "example")
.set("json", true)
);
const remote = _remote.init().asJSON();
remote.setURL("http://example.com/save");
const response = remote.post(
_val.map()
.set("data", "example")
.set("json", true)
);
remote = _remote.init().asJSON()
remote.setURL("http://example.com/save")
response = remote.post(
_val.map()
.set("data", "example")
.set("json", true)
)
remote = _remote.init().asJSON()
remote.setURL("http://example.com/save")
response = remote.post(
_val.map()
.set("data", "example")
.set("json", true)
)
val remote = _remote.init().asJSON()
remote.setURL("http://example.com/save")
val response = remote.post(
_val.map()
.set("data", "example")
.set("json", true)
)
final remote = _remote.init().asJSON()
remote.setURL("http://example.com/save")
final 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:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
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
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
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
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
val 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
final 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:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const remote = _remote.init();
remote.getHeader().set("KEY", "VALUE");
const remote = _remote.init();
remote.getHeader().set("KEY", "VALUE");
remote = _remote.init()
remote.getHeader().set("KEY", "VALUE")
remote = _remote.init()
remote.getHeader().set("KEY", "VALUE")
val remote = _remote.init()
remote.getHeader().set("KEY", "VALUE")
final 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:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
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
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
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
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
val 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
final 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:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
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());
}
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());
}
remote = _remote.init().asJSON()
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(
f"Failed with status: {response.statusCode()}",
response.content()
)
remote = _remote.init().asJSON()
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()
)
end
val remote = _remote.init().asJSON()
val 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()
)
}
final remote = _remote.init().asJSON()
final 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:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
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});
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});
download = _remote.asBinary().get(
"https://raw.githubusercontent.com/netuno-org/platform/main/docs/images/overview.png"
)
if not download.ok():
_log.error(f"File download status: {download.statusCode()}\n{download.toString()}")
_header.status(500)
_out.json(_val.map().set("result", False).set("error", "file-download-failed"))
_exec.stop()
file = download.file()
_log.info(f"Downloaded file name: {file.name()}")
file.save(_app.file(f"storage/filesystem/server/{file.name()}"))
file.save(_storage.filesystem("server", "other.png").file())
file.save(_os.file("/tmp/download.png"))
_out.json(_val.map().set("result", True))
download = _remote.asBinary().get(
"https://raw.githubusercontent.com/netuno-org/platform/main/docs/images/overview.png"
)
unless download.ok()
_log.error("File download status: #{download.statusCode()}\n#{download.toString()}")
_header.status(500)
_out.json(_val.map().set("result", false).set("error", "file-download-failed"))
_exec.stop()
end
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(_val.map().set("result", true))
val 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(_val.map().set("result", false).set("error", "file-download-failed"))
_exec.stop()
}
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(_val.map().set("result", true))
final 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()
}
final 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.saveis 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:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
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'});
}
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'});
}
file = _app.file("public/images/logo.png")
upload = _remote.asMultipartFormData().post(
"http://localhost:9000/services/file-receiver",
_val.map().set("file", file)
)
if upload.ok():
_log.info(f"Successfully uploaded: {file.name()}")
_out.json(_val.map().set("result", True))
else:
_log.error(f"File upload status: {upload.statusCode()}\n{upload.toString()}")
_header.status(500)
_out.json(_val.map().set("result", False).set("error", "file-upload-failed"))
file = _app.file("public/images/logo.png")
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(_val.map().set("result", true))
else
_log.error("File upload status: #{upload.statusCode()}\n#{upload.toString()}")
_header.status(500)
_out.json(_val.map().set("result", false).set("error", "file-upload-failed"))
end
val file = _app.file("public/images/logo.png")
val 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(_val.map().set("result", true))
} else {
_log.error("File upload status: ${upload.statusCode()}\n${upload.toString()}")
_header.status(500)
_out.json(_val.map().set("result", false).set("error", "file-upload-failed"))
}
final file = _app.file("public/images/logo.png")
final 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:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const file = _req.getFile("file");
_log.info(`Received file name: ${file.name()}`);
file.save(_storage.filesystem("server", "received.png").file());
_out.json({result: true});
const file = _req.getFile("file");
_log.info(`Received file name: ${file.name()}`);
file.save(_storage.filesystem("server", "received.png").file());
_out.json({result: true});
file = _req.getFile("file")
_log.info(f"Received file name: {file.name()}")
file.save(_storage.filesystem("server", "received.png").file())
_out.json(_val.map().set("result", True))
file = _req.getFile("file")
_log.info("Received file name: #{file.name()}")
file.save(_storage.filesystem("server", "received.png").file())
_out.json(_val.map().set("result", true))
val file = _req.getFile("file")
_log.info("Received file name: ${file.name()}")
file.save(_storage.filesystem("server", "received.png").file())
_out.json(_val.map().set("result", true))
final 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.