How to Make a Simple REST Call in Erlang
In the world of software development, interacting with REST APIs is a routine task, especially when building distributed systems and microservices. Erlang, a language known for its scalability and concurrency, can also be used to make RESTful calls despite not having built-in support for HTTP requests. Fortunately, Erlang’s httpc module, part of the inets library, provides a powerful way to interact with web services over HTTP.
Prerequisites
Before diving into the coding example, make sure you have Erlang installed and ready to use. You will also need access to the inets application, which is included by default in most Erlang distributions.
To start using the HTTP client (httpc), you’ll first need to ensure that the inets application is started.
application:start(inets).
This line of code starts the necessary HTTP client service for making HTTP requests.
Making a Simple GET Request
The most common use of REST APIs is to retrieve data from an endpoint. In this example, we’ll make a simple GET request to a public API that provides placeholder data for testing purposes: JSONPlaceholder.
GET Request:
-module(rest_example).
-export([get_post/1]).
get_post(PostId) ->
% Ensure the inets application is started
application:start(inets),
% Define the API URL with dynamic post ID
URL = "https://jsonplaceholder.typicode.com/posts/" ++ integer_to_list(PostId),
% Make the GET request
{ok, Response} = httpc:request(get, {URL, []}, [], []),
% Handle the response
case Response of
{ok, {{_, 200, _}, _Headers, Body}} ->
% Convert body from list to binary format
Body = list_to_binary(Body),
% Print the body to console
io:format("Response Body: ~s~n", [Body]);
{ok, {{_, StatusCode, _}, _Headers, _Body}} when StatusCode /= 200 ->
io:format("Error: Received non-200 status code ~p~n", [StatusCode]);
_Error ->
io:format("Error: Something went wrong~n")
end.
Explanation:
- The function
get_post/1takes aPostIdas an argument, constructs a URL, and sends a GET request to the API. - The
httpc:request/4function is used to send the GET request. The[]represents any headers or other options that may be needed, which is empty in this case. - The response body is converted to binary and printed to the console.
Making a POST Request
While GET requests are used to retrieve data, POST requests are used to send data to a server. In this example, we will make a POST request to send a new post to the JSONPlaceholder API.
POST Request:
-module(rest_example).
-export([create_post/1]).
create_post(PostData) ->
% Ensure the inets application is started
application:start(inets),
% Define the API URL
URL = "https://jsonplaceholder.typicode.com/posts",
% Set headers to indicate we are sending JSON data
Headers = [{"Content-Type", "application/json"}],
% Convert PostData to JSON format
Body = post_data_to_json(PostData),
% Make the POST request
{ok, Response} = httpc:request(post, {URL, Headers, "application/json", Body}, [], []),
% Handle the response
case Response of
{ok, {{_, 201, _}, _Headers, Body}} ->
io:format("Post Created Successfully: ~s~n", [list_to_binary(Body)]);
{ok, {{_, StatusCode, _}, _Headers, _Body}} when StatusCode /= 201 ->
io:format("Error: Received non-201 status code ~p~n", [StatusCode]);
_Error ->
io:format("Error: Something went wrong~n")
end.
post_data_to_json({Title, Body, UserId}) ->
% Create a JSON string for the post data
PostData = #{title => Title, body => Body, userId => UserId},
% Convert the Erlang map to a JSON string
jsx:encode(PostData).
Explanation:
- The
create_post/1function sends a POST request with JSON data to the API. - The
Headersspecify that the content type isapplication/json. - The
Bodyis created by converting thePostData(which is passed to the function) into a JSON string using thepost_data_to_json/1helper function. - The
jsx:encode/1function is used to encode an Erlang map as a JSON string. This requires thejsxlibrary, which you can install from Erlang’s package manager.
You would call the create_post/1 function with a tuple like {Title, Body, UserId} to create a new post.
Example call:
rest_example:create_post({"New Post", "This is a new post content", 1}).
Parsing JSON Responses
When working with REST APIs, the data returned is often in JSON format. Erlang does not have built-in support for JSON, but there are external libraries like jsx or jiffy to handle this.
In the code above, we use jsx:encode/1 to encode Erlang data into JSON when making the POST request. To parse the JSON response received from a GET request, you can use jsx:decode/1.
Parsing JSON Response:
{ok, {{_, 200, _}, _Headers, Body}} ->
Body = list_to_binary(Body),
{ok, ParsedJson} = jsx:decode(Body),
io:format("Parsed JSON: ~p~n", [ParsedJson]);
This will decode the JSON response body into an Erlang term (such as a list or map). You can then process the data as needed.
Handling Timeouts and Errors
When dealing with HTTP requests, it’s important to account for timeouts and handle errors appropriately. The httpc:request/4 function allows you to specify a timeout value.
Adding Timeout:
{ok, Response} = httpc:request(get, {URL, []}, [{timeout, 5000}], []),
This sets a 5-second timeout for the request. If the request doesn’t complete in that time, it will return an error.
Additionally, you should always handle errors using try-catch blocks to prevent crashes:
try
{ok, Response} = httpc:request(get, {URL, []}, [], [])
catch
error:Reason -> io:format("Error: ~p~n", [Reason])
end.
Conclusion
In this article, we have walked through the process of making both GET and POST REST calls in Erlang using the httpc module from the inets application. We also covered how to handle JSON data using external libraries like jsx, how to set timeouts, and how to gracefully handle errors.
By now, you should have a solid understanding of how to interact with REST APIs in Erlang and how to use this knowledge in your own projects. Whether you’re retrieving data from external services or sending data to a server, Erlang can handle RESTful communication efficiently.