• Skip to primary navigation
  • Skip to main content
  • Skip to footer
Code The Dream School
Code the Dream Labs Logo

Code The Dream School

Main hub for class materials for Code the Dream’s classes

  • Code the Dream Home

Search Code The Dream School

Backend 2 JSON/REST API, Swagger, Ajax

For this lesson, we will do part 1 of this tutorial: https://scotch.io/tutorials/build-a-restful-json-api-with-rails-5-part-one . An initial workspace with the starter version of the Rails application is available here.

Introductory Reading

We are going to create an API that communicates using REST protocols, and that exchanges JSON data. It’s a good idea to understand what REST is:

https://dzone.com/articles/introduction-to-rest-api-restful-web-services

And also, you will want to understand JSON:

Creating the API Server Application

The tutorial suggests that you do gem update rails. DO NOT DO THIS. That would take you to Rails 6, which has some complexities that we don’t want to take up at this time. Also, do not do the “rails new” command. Since you are starting from an existing workspace, that has been done for you.

Otherwise, carefully follow the instructions in the tutorial on creating the API in Rails. In completing the tutorial, I found the following corrections were necessary.

First, you must install httpie for use in testing. This is described here: https://httpie.org/ The steps to install it are:

On MacOS: brew install httpie

On Linux (inluding Vagrant linux under WIndows): sudo apt install httpie

Second, before you run Rspec, but after you do bin/rails db:migrate, you will need to do

bin/rails db:migrate RAILS_ENV=test

Third, at one point in the instructions, there is the following typo:

RSpec.configuration do |config|

It should be

RSpec.configure do |config|

To run Rspec, you should do:

bundle exec rspec

There are also some problems with the descriptions of the Todo and Item factories as described in the tutorial. The factory files should actually read as follows:

# spec/factories/todos.rb
FactoryBot.define do
  factory :todo do
    title { Faker::Lorem.word }
    created_by { Faker::Number.number(digits: 10) }
  end
end
# spec/factories/items.rb
FactoryBot.define do
  factory :item do
    name { Faker::Movies::StarWars.character }
    done { false }
    todo_id { nil }
  end
end

When you are finished with all of the steps in the tutorial, be sure you use httpie to test all of the operations, and using httpie create a few todos, and a few items belonging to each todo.

Adding Swagger

Swagger is a framework to document and test REST APIs. The following instructions describe how you add Swagger enablement to your API application.

First, add this line to the Gemfile:

gem ‘rswag’

Be sure you add it to the section before the development, test section in the Gemfile. Then do a bundle install. Then do

bin/rails g rswag:install

Swagger pages are defined using a YAML file, which is automatically generated from your Rspec test files — but only for test items that are of a certain format. Therefore, you must add the following lines to spec/requests/todos_spec.rb, just before the final end:

  path '/todos' do

    get('list todos') do
      tags 'Todos'
      response(200, 'successful') do

        after do |example|
          example.metadata[:response][:examples] = { 'application/json' => JSON.parse(response.body, symbolize_names: true) }
        end
        run_test!
      end
    end

    post('create todo') do
      tags 'Todos'
      consumes 'application/json'
      produces 'application/json'
      parameter name: :todo, in: :body, required: true, schema: {
        type: :object,
        required: %i[title created_by],
        properties: {
          title: { type: :string },
          created_by: { type: :string }
        }
      }
      response(201, 'successful') do
        let(:todo) { { title: 'Learn Elm', created_by: '1' } }
        run_test!
      end
    end
  end

  path '/todos/{id}' do
    parameter name: 'id', in: :path, type: :integer, description: 'id'

    get('show todo') do
      tags 'Todos'
      response(200, 'successful') do
        let(:id) { 5 }
        # after do |example|
        #   example.metadata[:response][:examples] = { 'application/json' => JSON.parse(response.body, symbolize_names: true) }
        # end
        run_test!
      end
    end

    put('update todo') do
      tags 'Todos'
      parameter name: :todo, in: :body, schema: {
        type: :object,
        properties: {
          title: { type: :string },
          content: { type: :string }
        }
      }
      response(204, "successful") do
        let(:id) { 5 }
        run_test!
      end
    end

    delete('delete todo') do
      tags 'Todos'
      response(204, "successful") do
        let(:id) { 5 }
        run_test!
      end
    end
  end

Also, add these lines to spec/requests/items_spec.rb, again just before the final end:

  path '/todos/{todo_id}/items' do

    parameter name: 'todo_id', in: :path, type: :integer, description: 'todo_id'

    get('list items') do
      tags 'Items'
      response(200, 'success') do
        run_test!
      end
    end

    post('create item') do
      tags 'Items'
      consumes 'application/json'
      produces 'application/json'
      parameter name: :item, in: :body, required: true, schema: {
        type: :object,
        required: %i[name],
        properties: {
          name: {type: :string},
          done: {type: :boolean}
        }
      }
      response(201, 'success') do
        let(:item) { { name: 'thisitem', done: false } }
        run_test!
      end
    end
  end

  path '/todos/{todo_id}/items/{id}' do
    parameter name: 'todo_id', in: :path, type: :integer, description: 'todo_id'
    parameter name: 'id', in: :path, type: :integer, description: 'id'

    get('show item') do
      tags 'Items'
      response(200,'success') do
        run_test!
      end
    end

    put('update item') do
      tags 'Items'
      consumes 'application/json'
      produces 'application/json'
      parameter name: :item, in: :body, required: true, schema: {
        type: :object,
        properties: {
          name: {type: :string},
          done: {type: :boolean}
        }
      }
      response(204,'success') do
        let(:item) { { name: 'changedName'} }
        run_test!
      end
    end

    delete('delete item') do
      tags 'Items'
      response(204,'success') do
        run_test!
      end
    end

  end

Finally, edit spec/swagger_helper.rb so that the servers section reads as follows:

  servers: [
    {
      url: "#{ENV['APPLICATION_URL']}"
    }
  ]

When you get all done with this, run

bundle exec rspec

To make sure that the test suites still work. Then type

bundle exec rake rswag:specs:swaggerize

Then start your server as usual. You will find that you have a new route, so that you can, from your browser, access http://localhost:3000/api-docs . Experiment with this page, trying out the various APIs and creating and updating todo and item entries.

AJAX

Now we will call the API using AJAX, from a front end application. There are several steps needed to get ready.

First, for those running Vagrant, you will have to edit your Vagrantfile, add a line at the bottom just before the final end, and restart Vagrant. You exit your vagrant ssh session and then do a vagrant halt, followed by a vagrant up after the Vagrantfile has been edited. The line you add is:

config.vm.network :forwarded_port, guest: 3001, host: 3001, host_ip: “127.0.0.1”

Second, we need to add an additional gem to our todos-api application, and change a configuration file. This is for security reasons having to do with something called CORS, or cross origin resource sharing. You add this line to your Gemfile:

gem ‘rack-cors’

Add it above the development, test section. Then you do bundle install. Then you add the following lines to config/application.rb:

    config.middleware.insert_before 0, "Rack::Cors" do
      allow do
        origins 'http://localhost:3000'
        resource(
          '*',
          headers: :any,
          methods: [:get, :patch, :put, :delete, :post, :options]
          )
      end
    end

These should be added just before the second to the last end statement in the file.

Now, you clone the following workspace for the ajax-sample application here. This sample application is fully functional but limited. Now you start BOTH applications. For todos-api, you start it as follows:

bin/rails s -p 3001

Or, if you are running vagrant:

bin/rails s -b 0.0.0. 0 -p 3001

For ajax-sample, you let it default to use port 3000:

bin/rails s

or if you are running vagrant:

bin/rails s -b 0.0.0.0

Now open the browser to localhost:3000. You will see a very basic page, On one line is a pushbutton to list the todos. If you push the button, an AJAX request is sent to retrieve the todo entries from the API application, and it comes back in JSON (looking kind of ugly). On another line is two entry fields and another pushbutton to create a todo. If you fill out the fields and push the the second button, it will create a todo and show the todo it created, again in JSON.

Study the javascript in the ajax-sample file app/views/ajax/home.html.erb . You will see that it creates a click event listener for the button, and then when it is clicked, it generates an XMLHttpRequest (AJAX), which causes the list of todos to be retrieved. Further, you will see an event listener for the second button. You will see that it also generates an Ajax request, this one to create a new todo. This second one posts a JSON body.

AJAX Assignment

Extend the home.html.erb file. Add an entry field and a second pushbutton for list items.. The entry field should be for the id of a todo entry. When the second pushbutton is clicked, it should retrieve all the items belonging to that todo entry, via an AJAX request. You will have to send the AJAX request to the URL http://localhost:3000/todos/todo_id/items where todo_id is the id from the entry field.

Once you get this working, extend the home.html.erb file further. Add entry fields for name and todo_id, and a checkbox for the boolean done, as well as a pushbutton for create item. You want to create an item belonging to the todo in the todo_id. You will need to send a POST request to the same URL as for the item list request, with a JSON encoded body for the item object to be created. Note that the todo_id is not in the JSON body because it is in the URL.

This reference may be helpful: https://www.sitepoint.com/guide-vanilla-ajax-without-jquery/

Footer

Copyright © 2025 Code the Dream School | All Rights Reserved | Privacy Policy