↖️ Show all posts

Ruby REST API client for Papierkram.de

I have written a small REST API client for the Papierkram.de API, which is a simple wrapper around the Faraday gem. Although it’s not perfect, it works well. For the HTTP client library, I chose httpx, which promises to be the future with blazing-fast performance, and it’s free too!

tldr; here’s the gem and here’s the code.

In this post, I would like to share some of the things I have learned while writing this gem.

Yet, there’s still some more documentation and testing to do. 😅

Testing with VCR and Minitest

I chose to use Minitest for testing, as I am a big fan of its simplicity and think it is a great choice for testing. One of the things I like about it is that it is just plain Ruby, with no DSL, magic, or weird syntax. While I’m not certain whether it is actually faster than RSpec, it certainly feels that way.

Minitest pairs very well with VCR for testing the API. Personally, I am not a big fan of mocking, so I prefer to record the API responses and replay them during testing. However, it is important to keep an eye on your recorded data to ensure that it is still valid.

Setting up Minitest and VCR is straightforward using minitest-vcr.

Here’s an example setup of Minitest’s test_helper.rb:

# $ cat app_name/test/test_helper.rb
require "minitest/autorun"
require "minispec-metadata"
require "vcr"
require "minitest-vcr"
require "faraday"

VCR.configure do |c|
  c.cassette_library_dir = 'test/cassettes'
  c.hook_into :faraday
end

MinitestVcr::Spec.configure!

It will record the request and the response. But it will also record the request headers. And if you don’t pay attention, you might end up with a recorded response that leaks personal or credential data.

So make sure to filter out any sensitive data from the request headers.

VCR brings some features that will help you with that. You can filter out parts of the request headers, like this:

interaction.response.headers['set-cookie'].first.split(';').first.split('=').last

OR you can filter out the whole request headers:

VCR.configure do |c|
  # ...
  c.filter_sensitive_data('<API_KEY>') do |interaction|
   interaction.request.headers['X-Http-Username']
  end
  # ...
end

Filter out a secret value coming from the environment:

VCR.configure do |c|
  # ...
  c.filter_sensitive_data('<API_KEY>') { ENV['PAPIERKRAM_API_KEY'] }
  # ...
end

This will help you avoid leaking sensitive data that you are aware of and can pin down exactly.

Filtering emails is a bit more tricky. You can filter out the whole email address in most cases something like this:

# tests ...
VCR.configure do |c|
  # ...
  c.before_record do |interaction|
    interaction.response.body.scan(/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}\b/).each do |email|
      interaction.response.body.gsub!(email, '<EMAIL>')
    end
  end
end

Clean your cassette files once in a while

I know there are auto-clean options in VCR, but in most cases, I can manage it myself. APIs usually don’t change that often or change gradually enough.

Deleting the cassettes a few times a year or after API changes have been communicated should be sufficient.

Check out the code of the gem

If you want to see how I implemented the gem, you can check out the code on GitHub. Please, provide feedback if you have any. I am always happy to learn something new. And if you have any questions, feel free to ask.

Conclusion

It’s always fun to put something together that you can use in your own projects. Working on this gem was a great learning experience for me. Implementing Minitest, VCR, configuring Faraday and Rubocop, direnv and setting up GitHub Actions was a great way to learn more about Ruby and its ecosystem. I want to maintain a Changelog.md file for this gem, so I (you) can keep track of the changes and document them. I will also add some more tests and documentation in the future.


Read next ➡️