Go REST

Ruby (Net::HTTP)

Net::HTTP idioms, persistent connections, and consuming the JSON response.

Ruby shipsNet::HTTP in the standard library, which is enough to call any JSON API without adding a gem. For larger apps,HTTParty orFaraday can clean up the boilerplate. This guide focuses onNet::HTTP because it is always available, then shows the same calls inHTTParty for contrast.

Setup

Open one persistent connection if you are calling the API repeatedly. TheNet::HTTP.start block keeps the TCP+TLS connection open for every call inside it, so a loop of 100 fetches becomes 100x cheaper.

require "net/http"
require "json"
require "uri"

API   = "https://gorest.co.in/public/v2"
TOKEN = ENV.fetch("GOREST_TOKEN")

BASE = URI(API)
HTTP = Net::HTTP.new(BASE.host, BASE.port)
HTTP.use_ssl = true
HTTP.start # open the TCP+TLS connection once and reuse it

Send the bearer token

Add the token to every request via a small helper. Nothing magical; it just stamps the same headers each time and parses the JSON response.

HEADERS = {
  "Authorization" => "Bearer #{TOKEN}",
  "Accept"        => "application/json",
  "Content-Type"  => "application/json"
}.freeze

def call(req)
  HEADERS.each { |k, v| req[k] = v }
  res = HTTP.request(req)
  [res.code.to_i, res.body && !res.body.empty? ? JSON.parse(res.body) : nil, res]
end

List users

def list_users(status: "active", page: 1)
  uri = URI("#{API}/users")
  uri.query = URI.encode_www_form(status: status, page: page)
  code, body, _ = call(Net::HTTP::Get.new(uri))
  raise "GET /users -> #{code}" unless code == 200
  body
end

list_users(status: "active").first(3)

Fetch a single user

def get_user(id)
  code, body, _ = call(Net::HTTP::Get.new(URI("#{API}/users/#{id}")))
  return nil if code == 404
  raise "GET /users/#{id} -> #{code}" unless code == 200
  body
end

Create, update, delete

Build the request, setbody to the JSON payload, and letcall handle the rest. The fields the API accepts on a user arename,email,gender, andstatus.

# Create
req = Net::HTTP::Post.new(URI("#{API}/users"))
req.body = JSON.dump(name: "Tenali Ramakrishna",
                     email: "tenali-#{Time.now.to_i}@example.com",
                     gender: "male",
                     status: "active")
code, created, _ = call(req)
raise "Create failed: #{code}" unless code == 201

# Partial update
req = Net::HTTP::Patch.new(URI("#{API}/users/#{created['id']}"))
req.body = JSON.dump(status: "inactive")
call(req)

# Delete
call(Net::HTTP::Delete.new(URI("#{API}/users/#{created['id']}")))

A 422 means validation failed. The body is an array of{ "field" => "...", "message" => "..." } hashes; surface them to your form layer:

req = Net::HTTP::Post.new(URI("#{API}/users"))
req.body = JSON.dump(name: "x")
code, errors, _ = call(req)

if code == 422
  errors.each { |e| puts "#{e['field']}: #{e['message']}" }
end

Pagination

The API puts pagination metadata in response headers (X-Pagination-Total,X-Pagination-Pages,X-Pagination-Page andX-Pagination-Limit). Loop on the page count rather than guessing when to stop:

def each_user(filters = {})
  return enum_for(:each_user, filters) unless block_given?
  page = 1
  loop do
    uri = URI("#{API}/users")
    uri.query = URI.encode_www_form(filters.merge(page: page))
    code, batch, res = call(Net::HTTP::Get.new(uri))
    raise "GET /users -> #{code}" unless code == 200
    batch.each { |u| yield u }
    break if page >= res["X-Pagination-Pages"].to_i
    page += 1
  end
end

each_user(status: "active").first(50).map { |u| u["email"] }

Retry on 429

The API rate-limits per access token. When you hit the ceiling, you get a 429 with anX-RateLimit-Reset header in seconds. Wrap requests in a small back-off helper:

def call_with_retry(req, attempts: 3)
  attempts.times do
    code, body, res = call(req)
    return [code, body, res] unless code == 429
    sleep [res["X-RateLimit-Reset"].to_i, 1].max
  end
  raise "Rate limited; gave up"
end

Same calls in HTTParty

If you prefer a higher-level client,HTTParty collapses every snippet above into one or two lines. It does the JSON parse and bearer header for you when you set the defaults on a class:

require "httparty"

class GoRest
  include HTTParty
  base_uri "https://gorest.co.in/public/v2"
  headers "Authorization" => "Bearer #{ENV.fetch('GOREST_TOKEN')}",
          "Content-Type"  => "application/json",
          "Accept"        => "application/json"

  def self.list_users(**query)
    get("/users", query: query).parsed_response
  end

  def self.create_user(payload)
    post("/users", body: payload.to_json).parsed_response
  end
end

Tips

Related guides

Keep going

Back to all guides Try it in the console