{"id":14955560,"url":"https://github.com/ntamvl/rails_5_api_tutorial","last_synced_at":"2025-10-01T18:31:39.218Z","repository":{"id":53743194,"uuid":"70682885","full_name":"ntamvl/rails_5_api_tutorial","owner":"ntamvl","description":"Building the Perfect Rails 5 API Only App \u0026 Documenting Rails-based REST API using Swagger UI","archived":false,"fork":false,"pushed_at":"2021-01-08T12:32:44.000Z","size":1066,"stargazers_count":66,"open_issues_count":3,"forks_count":20,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-01-14T11:21:15.973Z","etag":null,"topics":["api","rails","rails5","ruby"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ntamvl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-10-12T09:09:37.000Z","updated_at":"2024-02-21T10:48:02.000Z","dependencies_parsed_at":"2022-08-31T13:20:27.187Z","dependency_job_id":null,"html_url":"https://github.com/ntamvl/rails_5_api_tutorial","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ntamvl%2Frails_5_api_tutorial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ntamvl%2Frails_5_api_tutorial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ntamvl%2Frails_5_api_tutorial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ntamvl%2Frails_5_api_tutorial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ntamvl","download_url":"https://codeload.github.com/ntamvl/rails_5_api_tutorial/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234724179,"owners_count":18877147,"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":["api","rails","rails5","ruby"],"created_at":"2024-09-24T13:11:22.189Z","updated_at":"2025-10-01T18:31:33.715Z","avatar_url":"https://github.com/ntamvl.png","language":"JavaScript","readme":"# Building the Perfect Rails 5 API Only App\n\nThanks to the new `rails-api` gem that ships as part of the Rails 5 core, Rails is now an ideal candidate for building streamlined APIs quickly and easily.\n\nUntil now, arguably the best option for creating APIs in Ruby has been Grape, and while Grape is still a brilliant option (especially if you like to DIY), there are some great advantages to using Rails 5 in API mode, such as; ActiveRecord by default, a strong developer community, and having the asset pipeline and front end features available should you need them as your project evolves.\n\nThis how-to guide aims to help you get started the right way using Rails 5 to build the perfect API, and it comprises of the following sections:\n\n*   [Setting up Rails 5](#setting-up-rails-5)\n*   [Using RSpec for Testing](#using-rspec-for-testing)\n*   [Bulding Your API](#bulding-your-api)\n*   [Serializing API Output](#serializing-api-output)\n*   [Enabling CORS](#enabling-cors)\n*   [Versioning Your API](#versioning-your-api)\n*   [Rate Limiting and Throttling](#rate-limiting-and-throttling)\n*   [Authenticating Your API](#authenticating-your-api)\n*   [API documentation use Swagger UI](#documenting-rails-based-rest-api-using-swagger-ui)\n\n## Screenshot\n![Building the Perfect Rails 5 API Only using Swagger UI for documentation](https://c7.staticflickr.com/9/8548/29979999550_7980b15a45_b.jpg)\n\n## Setting up Rails 5\n\nFirst, make sure you are running Ruby 2.2.2+ or newer as it’s required by Rails 5.\n\nNow we generate new Rails API application by passing the `--api` directive to the `rails new` command:\n```\nrails __5.0.2__ api_app_name --api\n```\nThe next thing is to run `bundle` inside our app directory to install the default gems and setup the database:\n```\ncd api_app_name\nbundle install\n```\n\nand edit `config/database.yml` then run:\n```\nrails db:create\n```\n\nNice! Now we have a shiny new API only Rails app without any of the incumbent front end bloat, and all of the inherent Railsy goodness.\n\n## Using RSpec for Testing\n\nBefore going any further let’s setup [RSpec](//rspec.info) for spec testing our application. The reason why it’s good to setup RSpec first is that we can save a bit of time using the built-in RSpec generators to auto generate default model and controller specs for us each time we use `rails generate scaffold` to generate resources later on. To install RSpec, go ahead and add the [rspec-rails](https://github.com/rspec/rspec-rails) gem to your Gemfile in the `:development, :test` group:\n```\ngroup :development, :test do\n\n    # Use RSpec for specs\n    gem 'rspec-rails', '3.1.0'\n\n    # Use Factory Girl for generating random test data\n    gem 'factory_girl_rails'\nend\n```\n\nUpdate your bundle:\n```\nbundle\n```\n\nRun the RSpec installer:\n```\nbin/rails g rspec:install\n```\n\nFinally, you can get rid of the `test` directory in Rails, since we won’t be writing unit tests, but writing specifications instead.\n\n## Bulding Your API\n\nLets start building out our API controllers.\n\nWhen an app is created with the `--api` flag you can use the default scaffold generators to generate your API resources as normal, without the need for any special arguments.\n```\nbin/rails g scaffold user name email\n```\n\nThis will generate the following file structure:\n```\n          invoke  active_record\n       identical    db/migrate/20151222022044_create_users.rb\n       identical    app/models/user.rb\n          invoke    rspec\n          create      spec/models/user_spec.rb\n          invoke      factory_girl\n          create        spec/factories/users.rb\n          invoke  resource_route\n           route    resources :users\n          invoke  scaffold_controller\n       identical    app/controllers/users_controller.rb\n          invoke    rspec\n          create      spec/controllers/users_controller_spec.rb\n          create      spec/routing/users_routing_spec.rb\n          invoke      rspec\n          create        spec/requests/users_spec.rb\n```\n\nNote that no views are created since we are running in API mode.\n\nGo ahead and repeat the process with as many resources as you like, and once you’re done you can migrate and run the app:\n```\nbin/rails db:migrate\n\n# run the default server on port 3000\nbin/rails s\n```\n\nYour new API is now up and running on [http://localhost:3000](//localhost:3000). Sweet!\n\nYou’re not done yet though, there are still a bunch of important points for consideration…\n\n## Serializing API Output\n\nIn it’s current state our app will just spit out a JSON representation of every column in the database so we need a way to control what data gets served through the API.\n\nNormally we would use a front end templating engine such as `jbuilder` for this purpose, but since we’re not using views in our super streamlined API app, that’s not going to be an option.\n\nFortunately AMS (Active Model Serializers) is here to save the day. AMS provides a clean layer between the model and the controller that lets us to call `to_json` or `as_json` on the `ActiveRecord` object or collection as normal, while outputing our desired API format.\n\nGo ahead and add the `active_model_serializers` gem to your Gemfile:\n```\ngem 'active_model_serializers'\n```\n\nUpdate your bundle:\n```\nbundle\n```\n\nNow lets create a default serializer for our User model:\n```\nrails g serializer user\n```\n\nIn `app/serializers/user_serializer.rb`, we find this code:\n```ruby\nclass UserSerializer \u003c ActiveModel::Serializer\n    attributes :id\nend\n```\n\nNote that only the `:id` attribute is added by default. That’s not going to be much use to us, so go ahead and add the `:name` and `:email` attributes to the serializer:\n```ruby\nclass UserSerializer \u003c ActiveModel::Serializer\n    attributes :id, :name, :email\nend\n```\n\nIf your model has relationships just declare them on the serializer as you would any other attributes to be serialized in the output.\n\nYou may also need to include the `ActionController::Serialization` dependency in your controller like so:\n```ruby\nclass ApplicationController \u003c ActionController::API\n    include ActionController::Serialization\n\n    # ...\nend\n```\n\nNow when you hit and User related API endpoint only the attributes in the `UserSerializer` will be rendered. Nice!\n\nCheck the [active_model_serializers](https://github.com/rails-api/active_model_serializers) gem homepage for more detailed configuration options.\n\n## Enabling CORS\n\nIf you’re building a public API you’ll probably want to enable Cross-Origin Resource Sharing (CORS), in order to make cross-origin AJAX requests possible.\n\nThis is made very simple by the `rack-cors` gem. Just stick it in your Gemfile like so:\n```ruby\ngem 'rack-cors'\n```\n\nUpdate your bundle:\n```ruby\nbundle\n```\n\nAnd put something like the code below in `config/application.rb` of your Rails application. For example, this will allow GET, POST or OPTIONS requests from any origin on any resource.\n```ruby\nmodule YourApp\n    class Application \u003c Rails::Application\n\n    # ...\n\n    config.middleware.insert_before 0, \"Rack::Cors\" do\n        allow do\n        origins '*'\n        resource '*', :headers =\u003e :any, :methods =\u003e [:get, :post, :options]\n        end\n    end\n\n    end\nend\n```\n\nFor more detailed configuration options please see the gem documentation: https://github.com/cyu/rack-cors\n\n## Versioning Your API\n\nBefore releasing your public API into the wild, you should consider implementing some form of versioning. Versioning breaks your API up into multiple version namespaces, such as `v1` and `v2`, so that you can maintain backwards compatibility for existing clients whenever you introduce breaking changes into your API, simply by incrementing your API version.\n\nThis guide will show you how to setup versioning with the following URL format:\n\n    GET http://api.mysite.com/v1/users/\n\nUsing a subdomain instead of something like `/api/v1/users/` is just a preference, although both are easy to accomplish in Rails.\n\nWe can use a directory structure like this to keep our controller code clean by defining all our `v1` controllers within the `Api::V1` namespace:\n```\napp/controllers/\n.\n|-- api\n|   |-- v1\n|       |-- api_controller.rb\n|       |-- users_controller.rb\n|-- application_controller.rb\n```\n\nHere’s what the controllers look like:\n```ruby\n# app/controllers/api/v1/api_controller.rb\n\nmodule Api::V1\n    class ApiController \u003c ApplicationController\n    # Generic API stuff here\n    end\nend\n\n# app/controllers/api/v1/users_controller.rb\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    end\nend\n```\n\nNow let’s setup our `config/routes.rb` to tie everything together:\n```ruby\nconstraints subdomain: 'api' do\n    scope module: 'api' do\n    namespace :v1 do\n        resources :users\n    end\n    end\nend\n```\n\nThe `scope module: 'api'` bit lets us route to controllers in the API module without explicitly including it in the URL. However, the version `v1/` is part of the URL, and we also want to route to the V1 module, so we use `namespace`.\n\nNow you’re API routes are looking pretty sharp!\n\n## Rate Limiting and Throttling\n\nTo protect our API from DDoS, brute force attacks, hammering, or even to monetize with paid usage limits, we can use a Rake [middleware](//guides.rubyonrails.org/rails_on_rack.html) called `Rack::Attack`. The [rack-attack](https://github.com/kickstarter/rack-attack) gem was released by Kickstarter, and it allows us to:\n\n*   **whitelist**: Allowing it to process normally if certain conditions are true\n*   **blacklist**: Sending a denied message instantly for certain requests\n*   **throttle**: Checking if the user is within their allowed usage\n*   **track**: Tracking this request to be able to log certain information about our requests\n\nGet started by adding the dependency to your Gemfile:\n```ruby\ngem 'rack-attack'\n```\n\nUpdate your bundle:\n```ruby\nbundle\n```\n\nNow update your `config/application.rb` file to include it into your middleware stack:\n```ruby\nmodule YourApp\n    class Application \u003c Rails::Application\n\n    # ...\n\n    config.middleware.use Rack::Attack\n\n    end\nend\n```\n\nCreate a new initializer file in `config/initializers/rack_attack.rb` to configure your `Rack::Attack` rules. The example below is very basic, and it should give a good starting point although you may have different requirements altogether.\n```ruby\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\nFor a full list of configuration options check the [Rack::Attack](https://github.com/kickstarter/rack-attack) gem homepage.\n\nNow that your API is safe from brute force attacks and bad client code you can sleep a little better at night!\n\n## Authenticating Your API\n\nLet’s lock our API down with some authentication.\n\nAs a rule API’s should be stateless, and therefore should not have any knowledge of cookies or sessions.\n\nIf you require sessions then you should be looking at implementing some form of [OAuth](//oauth.net/2) based authentication, but that won’t be covered in this guide.\n\nA good way of authenticating API requests is using HTTP token based authentication, which involves clients including a API key of some sort in the HTTP `Authorization` header of each request, like so:\n```\nAuthorization: Token token=\"WCZZYjnOQFUYfJIN2ShH1iD24UHo58A6TI\"\n```\n\nFirst let’s update create a migration to add the `api_key` attribute to our `User` model:\n```ruby\nrails g migration AddApiKeyToUsers api_key:string\n```\n\nNow update the `User` model to include the following methods:\n```ruby\nclass User \u003c ActiveRecord::Base\n\n    # Assign an API key on create\n    before_create do |user|\n    user.api_key = user.generate_api_key\n    end\n\n    # Generate a unique API key\n    def generate_api_key\n    loop do\n        token = SecureRandom.base64.tr('+/=', 'Qrt')\n        break token unless User.exists?(api_key: token)\n    end\n    end\nend\n```\n\nOn the controller side we can implement the authentication using the built in `authenticate_or_request_with_http_token` Rails method.\n```ruby\nclass ApplicationController \u003c ActionController::Base\n    include ActionController::HttpAuthentication::Token::ControllerMethods\n\n    # Add a before_action to authenticate all requests.\n    # Move this to subclassed controllers if you only\n    # want to authenticate certain methods.\n    before_action :authenticate\n\n    protected\n\n    # Authenticate the user with token based authentication\n    def authenticate\n        authenticate_token || render_unauthorized\n    end\n\n    def authenticate_token\n        authenticate_with_http_token do |token, options|\n            @current_user = User.find_by(api_key: token)\n        end\n    end\n\n    def render_unauthorized(realm = \"Application\")\n    self.headers[\"WWW-Authenticate\"] = %(Token realm=\"#{realm.gsub(/\"/, \"\")}\")\n    render json: 'Bad credentials', status: :unauthorized\n    end\nend\n```\n\nNow we can test our authenticated API using `curl`:\n```\ncurl -H \"Authorization: Token token=PsmmvKBqQDOaWwEsPpOCYMsy\" http://localhost:3000/users\n```\n\n# Documenting Rails-based REST API using Swagger UI\n*Problem*\n\nYou built a REST API server using Rails and you need to document and test the endpoints.\n\n*The Setup*\n\nLet's assume the following:\n\n\u003e REST endpoint: `/api/v1/posts`\n\n\u003e Rails controller: `app/controllers/api/v1/posts_controller.rb`\n\n*Steps*\n\n**1 - Add the following to the Gemfile and run bundle afterwards.**\n\n### Swagger\n*Add this line to Gemfile*\n```\ngem 'swagger-docs'\n```\n*then run command*\n```\nbundle\n```\n**2 - Say you decide to structure your REST path in the following format: `/api/v1/{method}`. Edit `app/controllers/api/v1/posts_controller.rb` and add the following:**\n```ruby\n# app/controllers/api/v1/posts_controller.rb\n\nmodule Api\n  module V1\n    class PostsController \u003c ApplicationController\n\n      respond_to :json\n\n      swagger_controller :posts, 'Posts'\n\n      swagger_api :index do\n        summary 'Returns all posts'\n        notes 'Notes...'\n      end\n\n      def index\n        @posts = Post.all\n\n        render json: @posts, status: :ok\n      end\n    end\n  end\nend\n```\nThe swagger_api\u003c/code\u003e block represents the documentation for posts#index. When we run the command \"rails swagger:docs\" later, the info will be used to generate the posts.json file that Swagger UI uses to render the REST documentation.\n\n**3 - Generate `config/initializers/swagger.rb`**\n```ruby\n# config/initializers/swagger.rb\n\nclass Swagger::Docs::Config\n  def self.transform_path(path, api_version)\n    # Make a distinction between the APIs and API documentation paths.\n    \"apidocs/#{path}\"\n  end\nend\n\nSwagger::Docs::Config.base_api_controller = Api::V1::ApiController\n\nSwagger::Docs::Config.register_apis({\n  '1.0' =\u003e {\n    controller_base_path: '',\n    api_file_path: 'public/apidocs',\n    base_path: 'http://127.0.0.1:3000',\n    parent_controller: Api::V1::ApiController,\n    clean_directory: true\n  }\n})\n```\nWhen we run the command `rails swagger:docs` later, the info entered here will generate the `api-docs.json` file that is read by Swagger UI to generate the HTML page to display the documentation of the API.\n\nNote that we override the transformpath method in `Swagger::Docs::Config` to place the documentation files (which are located in `api-docs.json` and a *.json for each of the controllers) in a directory that is different from the actual API endpoints. This prevents any possible conflicts of URL since the path of documentation file generated from `rails swagger:docs` likely conflicts with the #index route. For example, if we don't override #transformpath both the documentation path for PostsController and the hosted API endpoint for posts#index will share the same URI path (`/api/v1/posts.json`), leading to a conflict.\n\n**4 - Also it a good practice not to check the generated Swagger documentation files into git. So we include the generated json files in .gitigore. Because all the generated files are saved under public/apidocs, it becomes easy to include those files in .gitignore.**\n\n*Ignore Swagger JSON files.*\n```\n/public/apidocs/\n```\n**5 - Generate the API docs. You must run the following command to generate new documentation json files everytime you change the API endpoints.**\n```\n$  rails swagger:docs\n```\nThe API documentation will be generated in the `public/apidocs` directory.\n\nRead this doc for more info on Swagger Docs.\n\n**6 - So far, we have configure our project to generate Swagger documentation files. We now need Swagger UI installed in our project. This isn't the final solution, but we can clone Swagger UI by creating a submodule in the public directory. This way Swagger UI can be served via the rails server.**\n```\n$ cd public\n$ git submodule add git@github.com:wordnik/swagger-ui.git swagger\n```\nOR\n```\ncd public\ngit submodule add git@github.com:swagger-api/swagger-ui.git swagger\n```\nRead this doc for more info on Swagger UI.\n\n**7 - As a convenience, we can add the following redirection in the routes.rb. This way, path /api will redirect us to the Swagger UI home page located in `public/swagger/dist/index.html`.**\n\nBy default, the Swagger UI home page retrieves the api-docs.json documentation file from http://petstore.swagger.wordnik.com/. We can override this behavior by appending a URI parameter url to the URL ie. `/swagger/dist/index.html?url=/apidocs/api-docs.json`.\n\n*edit `config/routes.rb`*\n```ruby\nget '/docs' =\u003e redirect('/swagger/dist/index.html?url=/apidocs/api-docs.json')\n```\n**8 - Run the Rails server.**\n```ruby\n$ rails s\n```\n**9 - Launch a web browser and go to `http://localhost:3000/docs`.**\n\n**10 - Should include SwaggerDoc into controller**\n```ruby\n# app/controllers/api/v1/api_controller.rb\n\nmodule Api::V1\n  class ApiController \u003c ApplicationController\n    Swagger::Docs::Generator::set_real_methods\n    include Swagger::Docs::ImpotentMethods\n\n    class \u003c\u003c self\n      Swagger::Docs::Generator::set_real_methods\n\n      def inherited(subclass)\n        super\n        subclass.class_eval do\n          setup_basic_api_documentation\n        end\n      end\n\n      private\n      def setup_basic_api_documentation\n        [:index, :show, :create, :update, :delete].each do |api_action|\n          swagger_api api_action do\n            param :header, 'Authorization', :string, :required, 'Authentication token'\n          end\n        end\n      end\n    end\n  end\nend\n```\nand in `app/controllers/api/v1/users_controller.rb`\n```ruby\n# app/controllers/api/v1/users_controller.rb\n\nmodule Api::V1\n  class UsersController \u003c ApiController\n    swagger_controller :users, \"User Management\"\n\n    def self.add_common_params(api)\n      api.param :form, \"user[name]\", :string, :optional, \"Name\"\n      api.param :form, \"user[email]\", :string, :optional, \"Email\"\n    end\n\n    swagger_api :index do\n      summary \"Fetches all User items\"\n      notes \"This lists all the active users\"\n    end\n\n    swagger_api :show do\n      summary \"Fetches user by id\"\n      notes \"Find user by id\"\n      param :path, :id, :integer, :optional, \"User Id\"\n      response :unauthorized\n      response :not_acceptable, \"The request you made is not acceptable\"\n      response :requested_range_not_satisfiable\n    end\n\n    swagger_api :create do |api|\n      summary \"Create a new User item\"\n      notes \"Notes for creating a new User item\"\n      Api::V1::UsersController::add_common_params(api)\n      response :unauthorized\n      response :not_acceptable\n      response :unprocessable_entity\n    end\n\n    # GET /v1/users\n    def index\n      render json: User.all\n    end\n\n    def show\n      user = User.find(params[:id])\n      if user.present?\n        render json: user\n      else\n        render json: { message: \"User can't be found!\" }\n      end\n    end\n\n    # POST /users\n    def create\n      @user = User.new(user_params)\n\n      if @user.save\n        render json: @user, status: :created, location: @user\n      else\n        render json: @user.errors, status: :unprocessable_entity\n      end\n    end\n\n    private\n    # Use callbacks to share common setup or constraints between actions.\n    def set_user\n      @user = User.find(params[:id])\n    end\n\n    # Only allow a trusted parameter \"white list\" through.\n    def user_params\n      params.require(:user).permit(:name, :email)\n    end\n\n  end\nend\n```\n\n## Rate Limiting per token\n#### Create file `config/initializers/throttle.rb`\n```ruby\n# config/initializers/throttle.rb\n\nrequire \"redis\"\n\nredis_conf  = YAML.load(File.join(Rails.root, \"config\", \"redis.yml\"))\nREDIS = Redis.new(:host =\u003e redis_conf[\"host\"], :port =\u003e redis_conf[\"port\"])\n\n# We will allow a client a maximum of 60 requests in 15 minutes. The following constants need to be defined in throttle.rb\nTHROTTLE_TIME_WINDOW = 15 * 60\nTHROTTLE_MAX_REQUESTS = 60\n```\n\nThe filter needs to be changed to respond with error messages when the rate limit is exceeded.\n```ruby\nclass ApplicationController \u003c ActionController::API\n  include ActionController::Serialization\n  include ActionController::HttpAuthentication::Token::ControllerMethods\n\n  before_action :authenticate\n  before_filter :throttle_token\n\n  protected\n\n  def authenticate\n    authenticate_token || render_unauthorized\n  end\n\n  def authenticate_token\n    authenticate_with_http_token do |token, options|\n      @current_user = User.find_by(api_key: token)\n      @token = token\n    end\n  end\n\n  def render_unauthorized(realm = \"Application\")\n    self.headers[\"WWW-Authenticate\"] = %(Token realm=\"#{realm.gsub(/\"/, \"\")}\")\n    render json: {message: 'Bad credentials'}, status: :unauthorized\n  end\n\n  def throttle_ip\n    client_ip = request.env[\"REMOTE_ADDR\"]\n    key = \"count:#{client_ip}\"\n    count = REDIS.get(key)\n\n    unless count\n      REDIS.set(key, 0)\n      REDIS.expire(key, THROTTLE_TIME_WINDOW)\n      return true\n    end\n\n    if count.to_i \u003e= THROTTLE_MAX_REQUESTS\n      render :json =\u003e {:message =\u003e \"You have fired too many requests. Please wait for some time.\"}, :status =\u003e 429\n      return\n    end\n    REDIS.incr(key)\n    true\n  end\n\n  def throttle_token\n    if @token.present?\n      key = \"count:#{@token}\"\n      count = REDIS.get(key)\n\n      unless count\n        REDIS.set(key, 0)\n        REDIS.expire(key, THROTTLE_TIME_WINDOW)\n        return true\n      end\n\n      if count.to_i \u003e= THROTTLE_MAX_REQUESTS\n        render :json =\u003e {:message =\u003e \"You have fired too many requests. Please wait for some time.\"}, :status =\u003e 429\n        return\n      end\n      REDIS.incr(key)\n      true\n    else\n      false\n    end\n  end\nend\n```\n\nLet’s go ahead and test this `test_throttle.sh`.\n```\nfor i in {1..300}\ndo\nprintf \"\\n------------------\\n\"\necho \"Welcome $i times\"\nprintf \"\\n\"\n# curl -i -H \"Authorization: Token token=3Hu9orST5sKDHUPJBwjbogtt\" http://localhost:3000/v1/users \u003e\u003e /dev/null\n# curl -i -H \"Authorization: Token token=3Hu9orST5sKDHUPJBwjbogtt\" http://10.1.0.201:3000/v1/users\ncurl -i -H \"Authorization: Token token=3Hu9orST5sKDHUPJBwjbogtt\" http://localhost:3000/v1/users\ndone\n```\n\n## How to run\n*Clone source from github: `git@github.com:ntamvl/rails_5_api_tutorial.git`*\n```\ncd\ngit clone git@github.com:ntamvl/rails_5_api_tutorial.git \u0026\u0026 cd rails_5_api_tutorial\nbundle install\n```\n*Edit `config/database.yml`*\n```\ndefault: \u0026default\n  adapter: postgresql\n  encoding: unicode\n  template: template0\n  pool: \u003c%= ENV.fetch(\"RAILS_MAX_THREADS\") { 5 } %\u003e\n  host: localhost\n  port: 5432\n  username: postgres\n  password: password\n\ndevelopment:\n  \u003c\u003c: *default\n  database: filter_api_development\n```\n*Create a new user to get token, type command `rails c`*\n```ruby\nu = User.create({name: \"Tam Nguyen\", email: \"ntamvl@gmail.com\"})\nap u\n```\n*next typing*\n```\nrails s\n```\n*then run in `Terminal`*\n```ruby\n# with [token] that taken on rails console\ncurl -H \"Authorization: Token token=[token]\" http://localhost:3000/v1/users\n```\n*example*\n```\ncurl -H \"Authorization: Token token=3Hu9orST5sKDHUPJBwjbogtt\" http://localhost:3000/v1/users\n```\n\n## Conclusion\n\nNow you have the keys to the castle, and all the basics for building an API the Rails way.\n\nHopefully then guide was helpful for you, and if you want any points clarified or just want to say thanks then feel free to use the comments below.\n\nCheers, and happy coding!\n\n---------------------------------------------\nRedis documentation for INCR command. [return]\n\nredis - A Ruby client that tries to match Redis’ API one-to-one, while still providing an idiomatic interface. It features thread-safety, client-side sharding, pipelining, and an obsession for performance. [return]\n\nRails’ before filter. [return]\n\nIETF: Additional HTTP Status Codes - 429 Too Many Requests. [return]\n\n*If you have questions or comments about this blog post, you can get in touch with me on Twitter @nguyentamvn*\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fntamvl%2Frails_5_api_tutorial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fntamvl%2Frails_5_api_tutorial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fntamvl%2Frails_5_api_tutorial/lists"}