Back

Step by Step Guide to Building a Harvest API Integration in Ruby

Aug 15, 20245 minute read

Introduction

Hey there, fellow Ruby enthusiast! Ready to dive into the world of time tracking with Harvest? In this guide, we'll walk through building a robust Harvest API integration. Harvest's API is a powerhouse for managing time entries, projects, and more. Let's get our hands dirty and build something cool!

Prerequisites

Before we jump in, make sure you've got:

  • Ruby 2.7+ installed
  • The httparty and dotenv gems
  • A Harvest account with API credentials

If you're missing any of these, take a quick detour to get set up.

Setting up the project

Let's kick things off:

mkdir harvest_integration cd harvest_integration bundle init

Add these to your Gemfile:

gem 'httparty' gem 'dotenv'

Then run bundle install. Easy peasy!

Authentication

Harvest uses OAuth 2.0, but we'll keep it simple with personal access tokens.

Create a .env file:

HARVEST_ACCESS_TOKEN=your_access_token
HARVEST_ACCOUNT_ID=your_account_id

Now, let's set up our API client:

require 'httparty' require 'dotenv/load' class HarvestClient include HTTParty base_uri 'https://api.harvestapp.com/v2' headers 'Authorization' => "Bearer #{ENV['HARVEST_ACCESS_TOKEN']}", 'Harvest-Account-Id' => ENV['HARVEST_ACCOUNT_ID'] end

Basic API Requests

Time to make our first request:

response = HarvestClient.get('/users/me') puts response.body

If you see your user info, you're on the right track!

CRUD Operations

Let's create a time entry:

time_entry = HarvestClient.post('/time_entries', body: { project_id: 12345, task_id: 67890, spent_date: Date.today.to_s, hours: 2 })

Retrieving time entries is a breeze:

entries = HarvestClient.get('/time_entries')

Updating and deleting follow a similar pattern. I'll leave those as a fun exercise for you!

Error Handling

Always expect the unexpected:

begin response = HarvestClient.get('/nonexistent_endpoint') rescue HTTParty::ResponseError => e puts "Oops! #{e.message}" end

Rate Limiting

Harvest caps us at 100 requests per 15 seconds. Let's play nice:

response = HarvestClient.get('/time_entries') remaining = response.headers['x-rate-limit-remaining'].to_i sleep 15 if remaining < 10

Pagination

Harvest uses cursor-based pagination. Here's a nifty way to handle it:

def get_all_pages(endpoint) results = [] page = 1 loop do response = HarvestClient.get(endpoint, query: { page: page }) results.concat(response['results']) break unless response['next_page'] page += 1 end results end

Advanced Features

Want to get fancy? Try setting up webhooks or diving into reporting. The Harvest API docs are your best friend here.

Testing

Don't forget to test! Here's a quick RSpec example:

RSpec.describe HarvestClient do it "fetches the current user" do VCR.use_cassette("current_user") do response = HarvestClient.get('/users/me') expect(response.code).to eq(200) expect(response['id']).to be_a(Integer) end end end

Conclusion

And there you have it! You've just built a solid foundation for a Harvest API integration. Remember, this is just the beginning. There's a whole world of time tracking goodness to explore.

Keep coding, stay curious, and may your commits always be green!