{"id":14955730,"url":"https://github.com/widgetry/rails-api-template","last_synced_at":"2025-10-01T01:31:32.856Z","repository":{"id":20852810,"uuid":"91099213","full_name":"Widgetry/rails-api-template","owner":"Widgetry","description":"Rails API Only Template","archived":false,"fork":false,"pushed_at":"2023-03-08T19:41:37.000Z","size":103,"stargazers_count":8,"open_issues_count":10,"forks_count":2,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-01-14T11:46:52.938Z","etag":null,"topics":["rails","rails-api","rails-application","rails-template","rails5-api"],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Widgetry.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-05-12T14:24:05.000Z","updated_at":"2024-10-21T09:40:05.000Z","dependencies_parsed_at":"2024-09-24T13:24:04.223Z","dependency_job_id":null,"html_url":"https://github.com/Widgetry/rails-api-template","commit_stats":{"total_commits":38,"total_committers":3,"mean_commits":"12.666666666666666","dds":0.5,"last_synced_commit":"759b4d5c73391fdbc2b7a1ce2ad3bf49d4482e81"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Widgetry%2Frails-api-template","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Widgetry%2Frails-api-template/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Widgetry%2Frails-api-template/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Widgetry%2Frails-api-template/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Widgetry","download_url":"https://codeload.github.com/Widgetry/rails-api-template/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234808946,"owners_count":18890088,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["rails","rails-api","rails-application","rails-template","rails5-api"],"created_at":"2024-09-24T13:11:38.404Z","updated_at":"2025-10-01T01:31:27.553Z","avatar_url":"https://github.com/Widgetry.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# How to Install\n\n1. Clone this repository\n2. Inside your database.yml rename the database to whatever you want, otherwise it will just be named `generic_api_development`, `generic_api_test`, `generic_api_production` for each environment respectively.\n3. Rename the top level module inside `application.rb` to whatever you want otherwise it will be named `GenericApi`\n4. Run `bundle install`\n5. Run `bundle exec rake db:create`\n6. Run `bundle exec rake db:migrate; RAILS_ENV=test bundle exec rake db:migrate`\n7. Boot up the server with `rails s` and navigate to localhost:3000 to see your new rails application\n\nIf you are interested in how this app was setup and what is included read on.\n\n# Setting up a Rails 5 API Only Application: Step by Step Guide\n\n## Introduction\n\n* Token based authentication with the `devise_token_auth` gem.\n* A namespaced API\n* Serialization with Active Model Serializers\n* Testing with rspec and factory girl\n* Useful rack middlewares like rack-attack for rate limiting and throttling with rack-cors.\n* Local development niceties such as mailcatcher, pry/pry-nav, and useful git commit hooks\n* Background jobs with Sidekiq\n* Caching with Redis\n\n## Initial Setup\n\n**Install Rails**\n\n`gem install rails`\n\n**Create a new API only project with Postgres as the selected database**\n\n`rails new project_name --api --database=postgresql`\n\n**Setup the database**\n\n```\ncd project_name\nrails db:setup\nrails db:migrate\n```\n\n**Confirm that the server is working**\n\nBoot up the server with the command `rails s` inside the project directory.\n\n**Add rspec, factory girl, and pry to test and development**\n\nLets go ahead and a add a couple gems that will be very useful for testing and development. I prefer to use rspec for testing purposes, but some of you might wan't to stick with minitest, if that's the case you can just ignore the `rspec-rails` gem. If not add `rspec-rails` and `factory_girl_rails`. FactoryGirl will let us easily create and mock test objects. The gem `pry-rails` provides a very handy interactive console when using rails c, `pry-byebug` gives you powerful break points and `pry-stack_expolorer` rounds out the package with a very solid stack explorer.\n\nOpen Gemfile, add the following. You can get rid of whatever else was in the :development, :test group\n\n```\ngroup :development, :test do\n  gem 'rspec-rails', '3.1.0'\n  gem 'factory_girl_rails'\n  gem 'pry-rails'\n  gem 'pry-byebug'\n  gem 'pry-stack_explorer'\nend\n```\n\nInstall the gems by running `bundle install`.\n\nFinish the rspec installation by running the command `rails generate rspec:install`.\n\nYou can go ahead and remove the test directory since we will be using /spec\n\n`rm -rf test`\n\nNow would be a good time to put everything under version control\n\n```\ngit init\ngit add --all\ngit commit -m 'Initial Commit'\n```\n\nSet up your git remotes if you would like. I trust you know how to do this.\n\n# Setting up token based authentication\n\nAdd `devise_token_auth` and `omniauth` gem to the Gemfile:\n\n```\ngem 'devise_token_auth'\ngem 'omniauth'\n```\n\nRun `bundle install` to install the gems.\n\n**Generating the user model with devise concerns and routes**\n\n`rails g devise_token_auth:install User auth`\n\nThe devise generator accepts two arguments the user class (in our case User) which is the name of the class to use for user authentication. The second argument is the mount path (in our case auth) which is path at which to mount the authentication routes.\n\nThe generator will create an initializer at `config/initializers/devise_token_auth.rb`, a User model at `app/models/user.rb`, a concern will be included in `app/controllers/application_controller.rb`, routes defined in `config/routes.rb`, and a migration to create the `users` table. You may want to tweak the migration to add columns to your liking. See [the github page](https://github.com/lynndylanhurley/devise_token_auth) for more information.\n\nRun `rails db:migrate` to add the users table as defined by the devise migrations.\n\nNow run `rails routes` to see the authentication routes that have been setup.\n\n**Mailer Configuration**\n\nSince devise uses mailers for account registration, deletion, password changing, etcetera, we need to configure our development environment to send mail. The first thing is we need to add the following to `config/initializers/devise.rb`\n\n```\nDevise.setup do |config|\n  # Using rails-api, tell devise to not use ActionDispatch::Flash\n  # middleware b/c rails-api does not include it.\n  config.navigational_formats = [:json]\nend\n```\n\nThe comment explains it all. We wan't devise to ignore the `ActionDispatch::Flash` middleware since it is not bundled in the Rails API mode middleware stack.\n\nNext Add the following to `config/environments/development.rb`\n\n```\nconfig.action_mailer.delivery_method = :smtp\nconfig.action_mailer.smtp_settings = { address: 'localhost', port: 1025 }\nconfig.action_mailer.default_url_options = { :host =\u003e 'localhost', port: '3000' }\n```\n\n**Token based authentication in action**\n\nLet's see some token authentication in action. Boot up your rails server with the `rails s` command.\n\nExamining the output of `rails routes` we can see that in order to regiser a new user we have to submit a POST request to the `/auth` routes. Let's do that using `curl`:\n\n`curl --data \"email=test@test.com\u0026password=testpassword1\u0026password_confirmation=testpassword1\u0026confirm_success_url=http://localhost:3000\" http://localhost:3000/auth`\n\n You should see a response that looks like the following:\n\n`{\"status\":\"success\",\"data\":{\"id\":1,\"provider\":\"email\",\"uid\":\"test@test.com\",\"name\":null,\"nickname\":null,\"image\":null,\"email\":\"test@test.com\",\"created_at\":\"2016-07-23T23:16:10.064Z\",\"updated_at\":\"2016-07-23T23:16:10.064Z\"}}`\n\nIf you examine your server logs, you should see that the action was processed and an email response was generated:\n\n```\n Started POST \"/auth\" for ::1 at 2016-07-23 19:16:09 -0400\nProcessing by DeviseTokenAuth::RegistrationsController#create as */*\n  Parameters: {\"email\"=\u003e\"test@test.com\", \"password\"=\u003e\"[FILTERED]\", \"password_confirmation\"=\u003e\"[FILTERED]\", \"confirm_success_url\"=\u003e\"http://localhost:3000\"}\n-- snip --\nDevise::Mailer#confirmation_instructions: processed outbound mail in 170.2ms\nSent mail to test@test.com (8.4ms)\nDate: Sat, 23 Jul 2016 19:16:10 -0400\nFrom: please-change-me-at-config-initializers-devise@example.com\nReply-To: please-change-me-at-config-initializers-devise@example.com\nTo: test@test.com\nMessage-ID:\nSubject: Confirmation instructions\nMime-Version: 1.0\nContent-Type: text/html;\n charset=UTF-8\nContent-Transfer-Encoding: 7bit\nclient-config: default\nredirect-url: http://localhost:3000\n\n\u003cp\u003eWelcome test@test.com!\u003c/p\u003e\n\n\u003cp\u003eYou can confirm your account email through the link below: \u003c/p\u003e\n\n\u003cp\u003e\u003ca href=\"http://localhost:3000/auth/confirmation?config=default\u0026confirmation_token=c9UbzsguMJ3BZkKZYC1A\u0026redirect_url=http%3A%2F%2Flocalhost%3A3000\"\u003eConfirm my account\u003c/a\u003e\u003c/p\u003e\n\nCompleted 200 OK in 666ms (Views: 0.6ms | ActiveRecord: 35.4ms)\n```\n\nGo ahead and copy the link into your broswer. Then, boot up a rails console using the `rails c` command, and run `User.first`. This should return the new test user you just created.\n\n**Setting up mailcatcher**\n\nReading the server logs to see what emails were rendered is not a great dev experience. Let's set up mailcatcher so that we can intercept emails delivered locally.\n\nInstall mailcatcher with the command `gem install mailcatcher`. Start mailcatcher by running `mailcatcher`. This will boot it up as a background daemon. Go to [http://localhost:1080/](http://localhost:1080/) to see the mailcatcher UI.\n\nNow lets try registering a new user.\n\n`curl --data \"email=test2@test.com\u0026password=testpassword1\u0026password_confirmation=testpassword1\u0026confirm_success_url=http://localhost:3000\" http://localhost:3000/auth`\n\nThis time we should be able to see the email in the mailcatcher UI.\n\n## Serialization with ActiveModelSerializers\nOur API will be responsible for rendering JSON responses, and that's about it. In order to build these JSON responses, we will use a gem called ActiveModelSerializers.\n\n**Install ActiveModelSerializers**\n\nAdd `gem 'active_model_serializers', '~\u003e 0.10.0'` to the Gemfile and run `bundle install`.\n\nAdd a config file in the location `config/initializers/active_model_serializer.rb` with the code:\n\n```\nActiveModelSerializers.config.adapter = :json_api\n```\n\n**Generating a user serializer**\n\nRun the command `rails g serializer user`. In the generated user serializer add the attributes :name, and :email so that the final code looks like this:\n\n```\nclass UserSerializer \u003c ActiveModel::Serializer\n  attributes :id, :name, :email\nend\n```\n\n**Setup an endpoint for serving user objects**\n\nNow that we have a user serializer, let's actually setup some endpoints that will serve us some user objects. This will be the begining of our API.\n\nLet's start by setting up the routes. We will scope these api routes to a module 'api' and then provide a v1 namespace for all our routes. Here is what we will add to the routes file:\n\n```\nscope module: 'api' do\n  namespace :v1 do\n    resources :users, only: [:index, :show]\n  end\nend\n```\n\nRun `rails routes` to see the routes that have been set up. You should see something like this:\n\n```\nv1_users GET      /v1/users(.:format)                    api/v1/users#index\nv1_user GET      /v1/users/:id(.:format)                api/v1/users#show\n```\n\nNow let's add an api controller from which all our other controllers in the API will inherit. The file should live in`app/controllers/api/v1/api_controller.rb`.\n\n```\nmodule Api::V1\n  class ApiController \u003c ApplicationController\n    # before_action :authenticate_user!\n  end\nend\n```\n\nNext add a users controller, with two actions index and show: `app/controllers/api/v1/users_controller.rb`\n\n```\nmodule Api::V1\n  class UsersController \u003c ApiController\n\n    # GET /v1/users\n    def index\n      render json: User.all\n    end\n\n    # GET /v1/users/{id}\n    def show\n      render json: User.find(params[:id])\n    end\n\n  end\nend\n```\n\nNow lets confirm that our controllers, routes, and serialization is working as expected:\n\nRun `curl http://localhost:3000/v1/users`, which will be routed to our index action, and you should see a response that looks something like this:\n\n```\n{\"data\":[{\"id\":\"1\",\"type\":\"users\",\"attributes\":{\"name\":null,\"email\":\"test@test.com\"}},{\"id\":\"2\",\"type\":\"users\",\"attributes\":{\"name\":null,\"email\":\"test2@test.com\"}}]}\n```\n\nRun `curl http://localhost:3000/v1/users/1`, which will be routed to our show action, and you should see a response like this:\n\n```\n{\"data\":{\"id\":\"1\",\"type\":\"users\",\"attributes\":{\"name\":null,\"email\":\"test@test.com\"}}}\n```\nGreat! We now have a bare minimum working API that serves some JSON. We can see that the attributes being returned are only the ones we specified in the serializer.\n\n## Authenticating our API\n\nWe setup devise, but we are not actually authenticating anywhere in our API! Let's do that now.\n\nDevise makes it easy for us. All we have to do is add the following `before_action` to `inapp/controllers/api/v1/api_controller.rb`:\n\n```\nbefore_action :authenticate_user!\n```\n\nSince we are poking around in our base api controller, and since we will exclusively be serving JSON to our clients, we can add the following to api controller:\n\n```\nrespond_to :json\n```\n\nGreat! Now if we try to hit the users controller with the same curl commands we used above we should get an authentication error. Let's test it out. Run `curl http://localhost:3000/v1/users/1`, and you should get the response\n\n```\n{\"errors\":[\"Authorized users only.\"]}\n```\n\n**Using tokens for authentication**\n\nWhen using token based authentication, we have to generate a token for every single request. Tokens are invalidated after each request to the API. During each request, a new token is generated. The access-token header that should be used in the next request is returned in the access-token header of the response to the previous request. For more information see the [devise_token_auth documentation](https://github.com/lynndylanhurley/devise_token_auth#about-token-management).\n\nWhen we run `rails routes` we should see a devise route that looks like this:\n\n```\nuser_session POST     /auth/sign_in(.:format)                devise_token_auth/sessions#create\n```\n\nThis is our login route. It return the initial auth token for when a user signs in. Let's test that by sending a post request to the endpoint with the appropriate payload:\n\n```\ncurl -XPOST -v -H 'Content-Type: application/json' localhost:3000/auth/sign_in -d '{\"email\": \"test@test.com\", \"password\": \"testpassword1\" }'\n```\n\nWe should get a response that looks like this:\n\n```\n* Connected to localhost (::1) port 3000 (#0)\n\u003e POST /auth/sign_in HTTP/1.1\n-- snip --\n\u003c Content-Type: application/json; charset=utf-8\n\u003c access-token: Avj8j2wQ4JAlFUDuPbS3fQ\n\u003c token-type: Bearer\n\u003c client: r4Pn4MLXvpCFTkwSc0HD7w\n\u003c expiry: 1470579487\n\u003c uid: test@test.com\n-- snip --\n* Connection #0 to host localhost left intact\n{\"data\":{\"id\":1,\"email\":\"the.kyle.maune@gmail.com\",\"provider\":\"email\",\"uid\":\"test@test.com\",\"name\":null,\"nickname\":null,\"image\":null,\"type\":\"user\"}}\n```\n\nAs you can see, we get the access-token, client, and uid in the headers of the response. The JSON response provides us with some attributes on the user object. Since this is a default devise route, it does not go through the user serializer we setup.\n\nNow we can use the validate_token route to confirm that our token is, well, valid:\n\n```\ncurl -XGET -v -H 'Content-Type: application/json' -H 'access-token: Avj8j2wQ4JAlFUDuPbS3fQ' -H 'client: r4Pn4MLXvpCFTkwSc0HD7w' -H \"uid: test@test.com\" localhost:3000/auth/validate_token`\n```\n\nThe success response:\n\n```\n* Connected to localhost (::1) port 3000 (#0)\n\u003e GET /auth/validate_token HTTP/1.1\n-- snip --\n\u003c Content-Type: application/json; charset=utf-8\n\u003c access-token: 2J6ygFVQrYHGy6aSH25D_g\n\u003c token-type: Bearer\n\u003c client: r4Pn4MLXvpCFTkwSc0HD7w\n\u003c expiry: 1470579646\n\u003c uid: test@test.com\n-- snip --\n{\"success\":true,\"data\":{\"id\":1,\"provider\":\"email\",\"uid\":\"test@test.com\",\"name\":null,\"nickname\":null,\"image\":null,\"email\":\"test@test.com\",\"type\":\"user\"}}\n```\n\nYou can see the new access was generated and sent back in the headers of the previous response. So let's hit the API with the token from the last request!\n\n```\ncurl -XGET -v -H 'Content-Type: application/json' -H 'access-token: 2J6ygFVQrYHGy6aSH25D_g' -H 'client: r4Pn4MLXvpCFTkwSc0HD7w' -H \"uid: test@test.com\" localhost:3000/v1/users/\n```\n\nAnd the sucessful response should look as follows:\n\n```\n{\"data\":[{\"id\":\"2\",\"type\":\"users\",\"attributes\":{\"name\":null,\"email\":\"test@test.com\"}},{\"id\":\"1\",\"type\":\"users\",\"attributes\":{\"name\":null,\"email\":\"test2@test.com\"}}]}\n```\n\nFantastic. At this point we have a bare minimum rails 5 api. Our routes are namespaced, users are authenticated with token based auth, and can sign up with email. We serialize our responses using `active_model_serializers`. This is most of what we need to get up and running.\n\n## Setting Up Specs\n\nWe sent some time setting up rspec and some useful debugging tools like pry-nav and factory girl, but we haven't actually written any tests yet. Why don't we go ahead and set up our first spec.\n\nFirst we need to set up factory girl. At the top of our `spec/spec_helper.rb` we need to explicitly require it as follows: `require 'factory_girl_rails'`. Then to gain access to the FactoryGirl DSL inside our specs we need to add the following line `config.include FactoryGirl::Syntax::Methods` within our config block. Then inside of our `.rspec` file we need to add the following line `--require rails_helper`. We can go ahead and get rid of the line `--require spec_helper`, since the `rails_helper` includes the `spec_helper`. If we don't do this RSpec Cannot find the Controllers and throws an Uninitialized Constant error. See [here](http://stackoverflow.com/questions/26288113/rspec-cannot-find-my-controllers-uninitialized-constant) for more information.\n\nNow lets set up a very simple test for the users controller in the location `spec/controllers/api/v1/users_controller_spec.rb`.\n\n```\nrequire 'spec_helper'\n\ndescribe Api::V1::UsersController do\n  describe \"GET #show\" do\n    before(:each) do\n      @user = FactoryGirl.create :user\n      auth_headers = @user.create_new_auth_token\n      request.headers.merge!(auth_headers)\n      get :show, id: @user.id\n    end\n\n    it 'responds with 200 status code' do\n      expect(response.code).to eq('200')\n    end\n  end\nend\n```\n\nAs you can see in the before block we hadd to generate an auth token for our user, and merge it into the request headers. See [here](https://github.com/lynndylanhurley/devise_token_auth/issues/75) for more information. Since you will likely be doing this in all your controller tests, it might be useful to abstract this to a convienience method inside `spec_helper`, for now I will leave it.\n\nWhen I tried running the test by executing `rspec spec/controllers/api/v1/users_controller_spec.rb` I got the following error:\n\n```\nDevise::MissingWarden:\n       Devise could not find the `Warden::Proxy` instance on your request environment.\n       Make sure that your application is loading Devise and Warden as expected and that the `Warden::Manager` middleware is present in your middleware stack.\n       If you are seeing this on one of your tests, ensure that your tests are either executing the Rails middleware stack or that your tests are using the `Devise::Test::ControllerHelpers` module to inject the `request.env['warden']` object for you.\n```\n\nWhat a useful error message! All I had to do was add the devise test helpers to `rails_helper.rb` inside the config block: `config.include Devise::Test::ControllerHelpers, type: :controller`.\n\nNow running the test should pass!\n\nLet's flesh this test out by adding another expectation for checking the response body:\n\n```\nit \"returns the serialized user attributes\" do\n  expect(JSON.parse(response.body)['data']['attributes']).to eq({\"name\"=\u003e\"John Doe\", \"email\"=\u003e\"test@test.com\"})\nend\n```\n\nAnd now let's add some tests for the index action:\n\n```\ndescribe Api::V1::UsersController do\n\n  -- snip --\n\n  describe 'GET #index' do\n    before(:each) do\n      @user = FactoryGirl.create :user\n      auth_headers = @user.create_new_auth_token\n      request.headers.merge!(auth_headers)\n      get :index, id: @user.id\n    end\n\n    it 'responds with 200 status code' do\n      expect(response.code).to eq('200')\n    end\n\n    it 'returns the serialized user attributes' do\n      expect(JSON.parse(response.body)['data'].length).to eq(1)\n      expect(JSON.parse(response.body)['data'].first['attributes']).to eq({'name'=\u003e'John Doe', 'email'=\u003e'test@test.com'})\n    end\n  end\nend\n```\n\n## Rack Middlewares\n\n**Use Rack CORS to allows cross origin resource sharing**\n\nIf your API will be public, you will need to enable Cross-Origin Resource Sharing. A resource makes a cross-origin HTTP request when it requests a resource from a different domain than the one which the first resource itself serves.\n\nAdd `gem 'rack-cors'` to the Gemfile and run `bundle install`\n\nThen add this to `application.rb`:\n\n```\nconfig.middleware.use Rack::Cors do\n  allow do\n    origins '*'\n    resource '*',\n      :headers =\u003e :any,\n      :expose  =\u003e ['access-token', 'expiry', 'token-type', 'uid', 'client'],\n      :methods =\u003e [:get, :post, :options, :delete, :put]\n  end\nend\n```\n\nWARNING: This will whitelist requests from any domain. Make sure to whitelist only the needed domains.\n\n**Rate limiting/throttling, blacklisting/whitelisting of IP's with rack-attack**\n\n`rack-attack` is a middleware that allows us rate limit, throttle, white list, and black list IP's. Trust me, you want this.\n\nAdd `gem 'rack-attack'` to the Gemfile and then run `bundle install`. Inside of `config/application.rb` add `config.middleware.use Rack::Attack`.\n\nNext, create a new initializer `config/initializers/rack_attack.rb`. I plucked the basic config from this [super helpful guide](http://sourcey.com/building-the-prefect-rails-5-api-only-app/#enabling-cors)\n\n```\nclass Rack::Attack\n\n  # `Rack::Attack` is configured to use the `Rails.cache` value by default,\n  # but you can override that by setting the `Rack::Attack.cache.store` value\n  Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new\n\n  # Allow all local traffic\n  whitelist('allow-localhost') do |req|\n    '127.0.0.1' == req.ip || '::1' == req.ip\n  end\n\n  # Allow an IP address to make 5 requests every 5 seconds\n  throttle('req/ip', limit: 5, period: 5) do |req|\n    req.ip\n  end\n\n  # Send the following response to throttled clients\n  self.throttled_response = -\u003e(env) {\n    retry_after = (env['rack.attack.match_data'] || {})[:period]\n    [\n      429,\n      {'Content-Type' =\u003e 'application/json', 'Retry-After' =\u003e retry_after.to_s},\n      [{error: \"Throttle limit reached. Retry later.\"}.to_json]\n    ]\n  }\nend\n```\n\nDepending on your needs, the 5 requests every 5 seconds may be a little too strict. For instance, if you are building a web client that makes several async requests, you may want to loosen the limit a bit.\n\n## Cacheing with Redis\n\nYou may have noticed that we are using the `ActiveSupport::Cache::MemoryStore.new` to back rack-attack. This is no good! This particular store puts everything into memory in the same process, so while the cache might be blazingly fast, it disappears when the process dissapears, and it's cache data cannot be shared across processes.\n\nI like to use Redis for cacheing. Some prefer Memcached. Both have their merits. But since we will be using sidekiq for job processing which requires Redis, and we may want to use action-cable, we might as well stick with Redis.\n\nFirst add `gem 'redis-rails'` to your Gemfile, and then run `bundle install`. Install and start redis using instructions [here](http://redis.io/topics/quickstart).\n\nIn `development.rb` get rid of the block of code that looks like this:\n\n```\n  # Enable/disable caching. By default caching is disabled.\n  if Rails.root.join('tmp/caching-dev.txt').exist?\n    config.action_controller.perform_caching = true\n\n    config.cache_store = :memory_store\n    config.public_file_server.headers = {\n      'Cache-Control' =\u003e 'public, max-age=172800'\n    }\n  else\n    config.action_controller.perform_caching = false\n\n    config.cache_store = :null_store\n  end\n```\n\nAnd then replace with this:\n\n```\n  if ENV['REDIS_URL']\n    config.action_controller.perform_caching = true\n    config.cache_store = :redis_store, ENV['REDIS_URL']\n  else\n    config.action_controller.perform_caching = false\n    config.cache_store = :null_store\n  end\n```\n\nThis way we can enable cacheing locally if we wish to test it. Usually in development cacheing is turned off, but if you want to enable it all you have to do is set the environment variable `REDIS_URL` to `redis://localhost:6379`.\n\nIn `production.rb` add\n\n```\n  config.cache_store = :redis_store, ENV['REDIS_URL']\n```\n\n## Background Job Processing with Sidekiq\nThis is sort of optional, but Sideiq is my go to for background job processing in ruby. It is extremely robust and efficient.\n\n\n#### Getting Started\nAdd `gem 'sidekiq'` to the Gemfile and bundle.\n\nCreate an `app/workers` directory, and try running a sample job:\n\n```\nclass SampleWorker\n  include Sidekiq::Worker\n  def perform()\n    # do something\n  end\nend\n```\n\nYou can kick it off in your console with `SampleWorker.perform_async` which will return a job ID.\n\n\n## Caching\n\nhttp://www.victorareba.com/tutorials/speed-your-rails-app-with-model-caching-using-redis\nhttps://medium.com/ruby-on-rails/easy-caching-with-rails-4-heroku-redis-5fb36381628#.o1a12hbzb\n\n\n## Nice to Haves\n\n**Setup Rack-attack to use Default Cache**\n\nNow that we have enabled redis we can enable rack-attack to use the default cache. In `config/initializers/rack_attack.rb` remove the line `Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new`. [By default, Rack::Attack will use whichever caching backend is configured as Rails.cache.](https://github.com/kickstarter/rack-attack/issues/102)\n\n**Set up a git commit hook to stop from committing binding.pry**\n\nI use binding.pry all over the place, and sometimes I forget to take it out before commiting. Here's a little pre-commit hook taken from the following [gist](https://gist.github.com/alexbevi/3436040) to prevent that from happening.\n\nInside the `/.git/hooks/pre-commit` file\n\n```\n#!/bin/bash\n\nFILES_PATTERN='\\.(rb)(\\..+)?$'\nFORBIDDEN='binding.pry'\n\ngit diff --cached --name-only | \\\n    grep -E $FILES_PATTERN | \\\n    GREP_COLOR='4;5;37;41' xargs grep --color --with-filename -n $FORBIDDEN \u0026\u0026 \\\n    echo 'COMMIT REJECTED' \u0026\u0026 \\\n    exit 1\n\nexit 0\n```\n\nMake it executable by running `chmod +x /.git/hooks/pre-commit`\n\nNow if you try to commit a binding pry it will reject the commit! Awesome!\n\n**Get rid of the \"Yay! You're on Rails!\" splash page**\n\nI like rails a whole bunch. But I don't need the world to know that when they go to my root url. For now let's just replace that with an empty JSON response.\n\nInside our `routes.rb` file lets add a root:\n\n```\nroot to: 'home#show'\n```\n\nAnd then add the following controller: `app/controllers/home_controller.rb` with\n\n```\nclass HomeController \u003c ApplicationController\n  def show\n    render json: {}\n  end\nend\n```\n\n**Change the sent from email in devise mailers**\n\nYou may have noticed the \"please-change-me-at-config-initializers-devise@example.com\" address in the from field of our devise mailers. We can change that by specifying the `mailer_sender` inside the devise config file:\n\n```\nDevise.setup do |config|\n  --snip--\n  config.mailer_sender = \"support@myapp.com\"\n\nend\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwidgetry%2Frails-api-template","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwidgetry%2Frails-api-template","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwidgetry%2Frails-api-template/lists"}