My ugly mug

Hi, I'm Thomas Cannon!

I work on Freckle with Amy Hoy and Thomas Fuchs, and I teach people how to add an extra layer of polish to their work. Previously I worked for BMW and Clemson University, and have a bunch of smaller apps I tinker with on the side. Follow me on Twitter!

Hey there!

If you’re just starting out with an API, you’ve probably tossed your API credentials somewhere as a class constant, like this:

class FreckleClient
  PERSONAL_ACCESS_TOKEN = "abcd12345"
  URL = "https://api.letsfreckle.com/v2"
  include HTTParty

  def get_entries
    self.class.get("#{URL}/entries", :headers => {
      "X-FreckleToken" => PERSONAL_ACCESS_TOKEN
    })
  end
end

This is completely valid for quickly hacking and trying things out. But once you’ve got a handle on APIs, you’ll want to start securely storing API keys for a few reasons:

The sanest approach that solves all these problems is to load the API credentials as environment variables in the app, which are stored in environment-specific YAML files. Thomas Fuchs introduced me to this pattern, and I’ve used it in all my apps since. The version below is for Rails 3+, let me know if you need a Rails 2.3 LTS version!

The code

# config/application.rb

module YourApp
  class Application < Rails::Application
    ...

    # Try loading a YAML file at `./config/env.[environment].yml`, if it exists
    # Kudos to Thomas Fuchs (http://mir.aculo.us) for the initial implementation
    def load_env_file(environment = nil)
      path = Rails.root.join("config", "env#{environment.nil? ? '' : '.'+environment}.yml")
      return unless File.exist? path
      config = YAML.load(ERB.new(File.new(path).read).result)
      config.each { |key, value| ENV[key.to_s] = value.to_s }
    end

    # Load environment variables. config/env.yml contains defaults which are
    # suitable for development. (This file is optional).
    load_env_file

    # Now look for custom environment variables, stored in env.[environment].yml
    # For development, this file is not checked into source control, so feel
    # free to tweak for your local development setup. Any values defined here
    # overwrite the defaults in `env.yml`
    load_env_file(Rails.env)
  end
end

Now to add the YAML files store our credentials!

# config/env.yml

# Just a test value to show overwriting
a: "hello"
# config/env.development.yml

# Just a test value to show overwriting
a: "derp"

freckle_personal_access_token: "abcd12345"

Now when we load the app, we’ll see these values loaded as environment variables!

$ rails c
irb(main):002:0> ENV['a']
=> "derp"
irb(main):003:0> ENV['freckle_personal_access_token']
=> "abcd12345"

Next up is to make sure the environment-specific YAML files are never stored in source control:

# .gitignore

# Ignore the environment-specific YAML files
/config/env.*.yml

Finally, we change our FreckleClient class to use the new environment variable and we’re good to go!

class FreckleClient
  URL = "https://api.letsfreckle.com/v2"
  include HTTParty

  def get_entries
    self.class.get("#{URL}/entries", :headers => {
      "X-FreckleToken" => ENV['freckle_personal_access_token']
    })
  end
end

Benefits

This approach has a number of benefits for development, security, and automated deployment:

Other Notes

  1. Each developer creates their own development API keys whenever possible. This stops devs from stepping on each other’s toes while working on a new feature or bugfixing.
  2. Production API keys are a closely guarded secret. A scrict “need to know basis” is the best approach here.
  3. Keep env.yml up-to-date as a template to make onboarding new devs easier. Seeing all the environment variables in one place makes it easier to get started.

Want more great, actionable guides to APIs and the trickier parts of being a developer? Enter your email below and get the next one for free! (you'll also get my Chatroom Survival Guide)