5 ways to make HTTP requests in Ruby

February 24, 2021
Written by
Reviewed by
Phil Nash
Twilion

Hello and welcome in this article! Today I’m going to show you five different ways to do HTTP requests in Ruby.

Before we start writing code I want to tell you how I’m happy I wrote this tutorial. Ruby on Rails is my favourite framework in the world and as every obsessive person I tend to always use the things I love.

Taking a step back to simple things as HTTP requests made me write and execute simple .rb files and it felt great to unleash the power of Ruby with my simple hands and not all the Rails magic that often secludes us from the basics of the language.

And what’s even better about the classics is they work in all the more complex environments so I’ll show you here how to do it with your own .rb file but this also works in any Ruby based framework.

That being said, let’s dive in and explore 5 different ways to make HTTP calls in any Ruby based program.

Requirements

To follow this tutorial you need:

  • Ruby installed on your machine. I’m using the latest Ruby version, currently 3.0.0, and rvm on OSX but use the method you prefer.
  • A text editor. I use sublime text or vim but feel free to use your favourite one.

What we’re going to achieve

We will make GET and POST requests using five different methods. We will do it using plain Ruby so for each request we will create a file with a .rb extension and execute it in our console using the Ruby command as

$ ruby file.rb

What we will see together

We will take a small tour through the following ruby libraries:

GET requests

We will use NASA's picture of the day API, APOD.

The API sends JSON as response for successful requests with response code 200. We will display the body of the response in our console.

Note on API keys: Here we will use the DEMO_KEY provided by NASA because we will just make a few requests and will not reach the API usage limit. If you plan to use it heavily, consider grabbing an API key for yourself.

{
  "date": "2019-07-20",
  "explanation": "Have you seen a panorama from another world lately? ...",
  "hdurl": "https://apod.nasa.gov/apod/image/1907/a11pan1040226lftsm.jpg",
  "media_type": "image",
  "service_version": "v1",
  "title": "Apollo 11 Landing Panorama",
  "url": "https://apod.nasa.gov/apod/image/1907/a11pan1040226lftsm600.jpg"
}

POST requests

For POST requests we are going to create articles using JSONPlaceholder, a demo tool for API requests.

We will simulate new articles creation with title foo, body bar and set the userID to 1. Of course this is all fake data for learning purposes.

There is no need for API key here and you should expect to see an output similar to this one in your console

{
  "title": "foo",
  "body": "bar",
  "userID": "1",
  "id": 101
}

The standard: net/HTTP

Net/HTTP is a default Ruby class.It is most of the time used along with URI.

Basic GET request

# nethttp.rb
require 'uri'
require 'net/http'

uri = URI('https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY')
res = Net::HTTP.get_response(uri)
puts res.body if res.is_a?(Net::HTTPSuccess)
$ ruby nethttp.rb

Okay, so here we tell URI which URL we want to query - this is a URL because the https:// is specified - then use the get_response method from the Net::HTTP library.

Finally we check if the request was a success and print the result in the console if it was.

Separated params

# nethttp2.rb
require 'uri'
require 'net/http'

uri = URI('https://api.nasa.gov/planetary/apod')
params = { :api_key => 'your_api_key' }
uri.query = URI.encode_www_form(params)

res = Net::HTTP.get_response(uri)
puts res.body if res.is_a?(Net::HTTPSuccess)

Here we do the same as before but we pass in the params separately as a hash. This is very useful when you need to pull params from different sources or when they become numerous. Hashes are a widely used object in Ruby and are easy to format in order to make the code readable, do not hesitate to make use of them.

Once we have defined our params, we set the uri.query to our params that we encode in a format compatible for the web as a form using encode_www_form. And that’s all about the differences.

POST request

# nethttp3.rb
require 'uri'
require 'net/http'

uri = URI('https://jsonplaceholder.typicode.com/posts')
res = Net::HTTP.post_form(uri, 'title' => 'foo', 'body' => 'bar', 'userID' => 1)
puts res.body  if res.is_a?(Net::HTTPSuccess)

For this request we use the post_form method of Net::HTTP, and after the uri we give our params in the key value form. Then we print the response body if the request was a success.

Of course do not hesitate to dive in the docs, and here is a cheat sheet for quick reference. For complex usage you might want to use the OpenURI wrapper which also wraps two other useful libraries.

httparty: Make requests fun again!

httparty is a popular gem for making requests. Install it with

$ gem install httparty
# httparty.rb
require 'httparty'

response = HTTParty.get('https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY')
puts response.body if response.code == 200

For this request we just need to tell httparty the method we want to use get here, and we just provide it with the full URL.

The response object provided by httparty contains lots of useful methods. Here we use the code method to check if the request was a success and if so we use the body method to print the JSON body the NASA’s API sent us back. But there are tons of other, for instance response.parsed_response will give you the parsed JSON.

POST request, the classy way!

One widely used but totally optional implementation of the gem is to make a class out of it. And yes, we can do that too in just one .rb file!

# httparty2.rb
require "httparty"

class PostManager
  include HTTParty
  base_uri 'https://jsonplaceholder.typicode.com'

  def initialize()

  end

  def create_post(title, body, user_id)
    params = { body: { title: title, body: body, userID: user_id } }
    self.class.post("/posts", params).parsed_response
  end
end

post_manager = PostManager.new()
p post_manager.create_post("foo", "bar", 1)

Here we create a PostManager class, and set our base_uri to the base URL of the API we want to make requests at.

This class has a method create_post which takes 3 parameters. The title, body and userID we’ve seen before.

Then we use the post method on self.class, giving the parameters we get from the method initialization.

Then all we have to do is to create a new instance of this class with PostManager.new()

And then use the create_post method with our arguments.

You can see the class also has an initialize method you can take advantage of when building more complex use cases.

You can also add other methods. In this context it would make sense to add a read_post using a get method or a delete_post method using the delete method. The limit is your imagination.

Bonus

httparty comes with a CLI, which allows you, once the gem is installed, to use it straight from your console:

$ httparty your_url

HTTP (The Gem! a.k.a. http.rb)

http.rb is another popular gem. Install it with

$ gem install http
# http.rb
require "http"

response = HTTP.get("https://api.nasa.gov/planetary/apod", :params => {:api_key => "DEMO_KEY"})
p response.parse

We use the get method of HTTP and give our url and pass the params in as a hash named params.

Using .parse on our response object will parse the JSON into a hash.

POST request

# http2.rb
require "http"

response = HTTP.post("https://jsonplaceholder.typicode.com/posts", :form => {'title' => 'foo', 'body' => 'bar', 'userID' => 1})
p response.parse

Bonus response handling

Note that all the responses we have printed have been parsed with .parse because they were JSON and we knew it. But there are other useful methods you can use to manipulate the response body.

  • to_s. Yes calling the “to string” Ruby method will put all the content’s response in a string if used as response.body.to_s.
  • readpartial: very useful for reading html documents line by line. Use it as response.body.readpartial. You will need to call this method as much as there are chunks in the html document you are reading. Learn more about it here.

The concurrent HTTPX

What’s differentiating the most httpx from the other gems is that it's HTTP2 by default, making it great at pipelining and concurrent requests. Install it with

$ gem install httpx
# httpx.rb
require "httpx"

response = HTTPX.get("https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY")
puts response.body if response.status == 200

httpx in its basic functionalities works in a straightforward way. We give the full URL with params and use the object method named after the HTTP verb we want to use.

POST request

# httpx2.rb
require "httpx"

response = HTTPX.post("https://jsonplaceholder.typicode.com/posts", :json => {'title' => 'foo', 'body' => 'bar', 'userID' => 1})
puts response.body if response.status == 201

In this post request we use the post method from HTTPX and pass the params and a hash.

We print the body if the response status is 201, which means created in the HTTP standard.

Simultaneous multiple GET requests

# httpx3.rb
require 'httpx'

base_url = "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY"
response, response2 = HTTPX.get(base_url, base_url + "&date=2019-07-20")
puts response.body if response.status == 200
puts response2.body if response2.status == 200

Here we store the base url of the NASA API with our API key and tell HTTPX to perform two simultaneous get requests, one to the url we already requested, and another to the same url with an additional parameter: date. This will give us information about the image of the day for the specific date we entered as a parameter.

Then we print in our console the two response bodies we just retrieved.

This is a basic usage and definitely can be extended to match bigger needs.

Faraday

Install Faraday, yet another widely used gem with

$ gem install faraday
# faraday.rb
require "faraday"

response = Faraday.get("https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY")
p response.body if response.status == 200

If you have followed so far, you probably already have guessed what’s going on here. Using Faraday get method with our NASA’s API url, we then print the response body if the status code equals 200.

You can also see in your console that the body isn’t handled as JSON by design and is displayed as a string.

POST request with URI encoded parameters

# faraday2.rb
require "faraday"
require 'uri'

params = {title: "foo", body: "bar", userID: 1}
encoded_params = URI.encode_www_form(params)
response = Faraday.post("https://jsonplaceholder.typicode.com/posts", encoded_params)
p response.body if response.status == 201

We define our params as a hash. Then we set the encoded_params to our params encoded in a web form compatible format using encode_www_form method from URI.

URI is a standard Ruby library we’ve seen when using net/HTTP. Then we use the Faraday.post method and print the response.

POST request with do block

# faraday3.rb
require "faraday"

response = Faraday.post "https://jsonplaceholder.typicode.com/posts" do |request|
  request.body = URI.encode_www_form({title: "foo", body: "bar", userID: 1})
end
p response.body if response.status == 201

Another way to pass additional settings to our Faraday requests is the usage of the do block.

Using the object created in the do block we use the .body object method to add our params encoded using URI.

There are other useful methods like request.headers['Content-Type'] = 'application/json' here setting the request’s content type, or request.params.

The reason behind Faraday’s popularity is the available middleware. They provide many useful capabilities for dealing with authentication, xml and yml formats and other cool things you can discover more about here.

Using these in any Ruby framework based application

In order to use the methods mentioned above in Ruby on Rails, Sinatra, Hanami...etc, you just need to remove the require X line and instead add in your application’s Gemfile:

gem 'X'

Do not forget to run $ bundle install in your console when you edit your Gemfile.

To sum up

All the solutions displayed here are pretty equivalent for simple requests. They differentiate at the advanced usage level.

Keep in mind that they all were designed to match specific needs and these specifications by design should be a key point in your decision on which to choose when it comes to more complex use cases.

Of course there is a lot more to say about each of the gems I showed you here and to know about the full capabilities of each you should read the documentations and experiment by yourself. 

I personally tend to use httparty the most, because it already was widely used when I learnt Ruby, it was easy to use and understand and it has tons of examples in StackOverflow. But honestly? I’m biased, they got me with their catchphrase: Ain't no party like a httparty, because a httparty don't stop!

I’m also keeping an eye on httpx because the gem is still young and already has interesting features.

Want to change your mind for a while before getting back to those requests ? Why not learn how to download image files or play with WhatsApp and Ruby!

 

Valériane Venance is a Developer Evangelist at Twilio. Leave her a message at vvenance@twilio.com or on Twitter if you’ve built something cool with Ruby. She’d love to hear about it!