How to make REST API in Rails
Making a simple API in rails
We have seen in the part-1 of this post "How to make an end point through ReST to get the user info as Json". If you haven't covered up part-1, please go through it to have a better understanding of this tutorial.
So far, we have created an end point for show action which look like:
http://localhost:3000/api/v1/users/1
Now we will make more endpoints to create, update and delete a user.
Create action in Rails API
Let's code "create" action in app/controllers/api/v1/users_controller.rb
class Api::V1::UsersController < ApplicationController
respond_to :json
skip_before_filter :verify_authenticity_token
def show
respond_with User.find(params[:id])
end
def create
user=User.new(user_params)
# if the user is saved successfully than respond with json data and status code 201
if user.save
render json: user, status: 201
else
render json: { errors: user.errors}, status: 422
end
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
Here we have another private method user_params to sanitize the attributes to be assigned through mass-assignment. After successful creation of the user we are responding with the json data and status code 201.
Now, we need to tell our config/routes.rb file that we also have create action. Let's add create action in config/routes.rb file
Rails.application.routes.draw do
# API routes path
namespace :api, defaults: { format: :json } do
namespace :v1 do
#devise_for :users
resources :users, :only => [:show, :create]
end
end
end
Hurray!!! We have successfully created the endpoint to create a user which you can check through by sending a post request through POSTMAN (A tool to check API endpoints).
POST request through postman to create a user
Response of the POST request
Update action in Rails API
Time to update the user being already created. The update action responds to PUT/PATCH request.
Let's add update action to app/controllers/api/v1/users_controller.rb
class Api::V1::UsersController < ApplicationController
respond_to :json
skip_before_filter :verify_authenticity_token
def show
respond_with User.find(params[:id])
end
# Creating users
def create
user=User.new(user_params)
if user.save
render json: user, status: 201
else
render json: { errors: user.errors}, status: 422
end
end
# Updating users
def update
user = User.find(params[:id])
if user.update(user_params)
render json: user, status: 200
else
render json: { errors: user.errors }, status: 422
end
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
You might have guessed the next step, yes, now we need to add the update action in config/routes.rb file too. So let's do it.
Rails.application.routes.draw do
# API routes path
namespace :api, defaults: { format: :json } do
namespace :v1 do
#devise_for :users
resources :users, :only => [:show, :create, :update]
end
end
end
Now Let's test our update action.
Update action through POSTMAN
Update action response
Destroy action in Rails API
Destroy action is used to delete a record and here we are going to delete a user. So let's add the destroy action in app/controllers/api/v1/users_controller.rb
class Api::V1::UsersController < ApplicationController
respond_to :json
skip_before_filter :verify_authenticity_token
def show
respond_with User.find(params[:id])
end
# Creating users
def create
user=User.new(user_params)
if user.save
render json: user, status: 201
else
render json: { errors: user.errors}, status: 422
end
end
# Updating users
def update
user = User.find(params[:id])
if user.update(user_params)
render json: user, status: 200
else
render json: { errors: user.errors }, status: 422
end
end
# Deleting users
def destroy
user = User.find(params[:id])
user.destroy
head 204
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
Here we are deleting the user with a particular ID which is being passed as a parameter and after successful deletion of the user we are giving 204 code which means the server has processed the request and doesn't return any content. We may also respond with 200 status code but this is more suitable.
Remember to update config/routes.rb file
Rails.application.routes.draw do
# API routes path
namespace :api, defaults: { format: :json } do
namespace :v1 do
#devise_for :users
resources :users, :only => [:show, :create, :update, :destroy]
end
end
end
Destroy action with POSTMAN
Destroy action response
Congratulations!! you are done with the CRUD part of a ReSTful API in Rails.
Further you can dockerize this Rails app - Follow this post to dockerize this API
where do I download of the project???
ReplyDeleteWe suggest you to write code step by step following this tutorial for better learning and understanding but if you directly want to get the app, here is the project - https://github.com/AjeetK/railsapi
Deleterespond_to :json not working for me
ReplyDeleteany other syntax?
What is the error you are getting? Please paste your error trace here.
DeleteIt works now after I commented some other code!
ReplyDeleteclass Api::V1::ServerdetailsController < ApplicationController
#class ServerdetailsController < ApplicationController
respond_to :json
before_action :set_serverdetail, only: [:show, :edit, :update, :destroy]
# GET /serverdetails
def index
@serverdetails = Serverdetail.all
render json: @serverdetails
end
# GET /serverdetails/1
def show
# render json: @serverdetail
end
# GET /serverdetails/new
def new
@serverdetail = Serverdetail.new
end
# GET /serverdetails/1/edit
def edit
end
# POST /serverdetails
def create
@serverdetail = Serverdetail.new(serverdetail_params)
if @serverdetail.save
# render json: @serverdetail, redirect_to @serverdetail, notice: 'Serverdetail was successfully created.'
else
# render json: @servdetail.errors, render :new
end
end
# PATCH/PUT /serverdetails/1
def update
if @serverdetail.update(serverdetail_params)
# render json: @serverdetail,redirect_to @serverdetail, notice: 'Serverdetail was successfully updated.'
else
render :edit
end
end
# DELETE /serverdetails/1
def destroy
@serverdetail.destroy
redirect_to serverdetails_url, notice: 'Serverdetail was successfully destroyed.'
end
private
# Use callbacks to share common setup or constraints between actions.
def set_serverdetail
@serverdetail = Serverdetail.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def serverdetail_params
params.require(:serverdetail).permit(:servername, :cpu, :disk, :process)
end
end
ubuntu@rails-dev-box:/vagrant/r$ curl localhost:3000/api/v1/serverdetails/
[{"id":1,"servername":"wserver","cpu":23,"disk":34,"process":2,"created_at":"2017-10-31T04:21:55.520Z","updated_at":"2017-10-31T04:21:55.520Z"},{"id":2,"servername":"servertaiwan","cpu":34,"disk":344,"process":3,"created_at":"2017-10-31T04:22:24.390Z","updated_at":"2017-10-31T04:22:24.390Z"},{"id":3,"servername":"333yur","cpu":77,"disk":77,"process":67,"created_at":"2017-10-31T04:24:48.682Z","updated_at":"2017-10-31T04:24:48.682Z"},{"id":4,"servername":"vbvvb","cpu":87,"disk":45,"process":54,"created_at":"2017-10-31T06:45:53.295Z","updated_at":"2017-10-31T06:45:53.295Z"}]ubuntu@rails-dev-box:/vagrant/r$
ubuntu@rails-dev-box:/vagrant/r$
The above is the output to curl. What is the curl command to update the values of those existing servers . The app(with api) is to receive curl inputs containing server(linux) command outputs for cpu usage, disk usage etc..Do you know how such values means the output of linux commands could be passed using curl? say I will set a cron job in linux server which will send the values of cpu , disk usage etc every 3 hours or so?
You can write a bash script which collects these metrics and store it in json format which is compatible with your api post data. Then you can pass this json data with -d option to your curl request with method post.
Deleteoops sorry the api part still does not work with respond_to :json
ReplyDeletehere is the full trace
Routing Error
undefined method `respond_to' for Api::V1::ServerdetailsController:Class
Rails.root: /vagrant/r
Application Trace | Framework Trace | Full Trace
app/controllers/api/v1/serverdetails_controller.rb:3:in `'
app/controllers/api/v1/serverdetails_controller.rb:1:in `'
activesupport (5.1.4) lib/active_support/dependencies.rb:476:in `load'
activesupport (5.1.4) lib/active_support/dependencies.rb:476:in `block in load_file'
activesupport (5.1.4) lib/active_support/dependencies.rb:661:in `new_constants_in'
activesupport (5.1.4) lib/active_support/dependencies.rb:475:in `load_file'
activesupport (5.1.4) lib/active_support/dependencies.rb:374:in `block in require_or_load'
activesupport (5.1.4) lib/active_support/dependencies.rb:36:in `block in load_interlock'
activesupport (5.1.4) lib/active_support/dependencies/interlock.rb:12:in `block in loading'
activesupport (5.1.4) lib/active_support/concurrency/share_lock.rb:149:in `exclusive'
activesupport (5.1.4) lib/active_support/dependencies/interlock.rb:11:in `loading'
activesupport (5.1.4) lib/active_support/dependencies.rb:36:in `load_interlock'
activesupport (5.1.4) lib/active_support/dependencies.rb:357:in `require_or_load'
activesupport (5.1.4) lib/active_support/dependencies.rb:510:in `load_missing_constant'
activesupport (5.1.4) lib/active_support/dependencies.rb:202:in `const_missing'
activesupport (5.1.4) lib/active_support/inflector/methods.rb:284:in `const_get'
activesupport (5.1.4) lib/active_support/inflector/methods.rb:284:in `block in constantize'
activesupport (5.1.4) lib/active_support/inflector/methods.rb:267:in `each'
activesupport (5.1.4) lib/active_support/inflector/methods.rb:267:in `inject'
activesupport (5.1.4) lib/active_support/inflector/methods.rb:267:in `constantize'
activesupport (5.1.4) lib/active_support/dependencies.rb:582:in `get'
activesupport (5.1.4) lib/active_support/dependencies.rb:613:in `constantize'
actionpack (5.1.4) lib/action_dispatch/http/request.rb:85:in `controller_class_for'
actionpack (5.1.4) lib/action_dispatch/http/request.rb:78:in `controller_class'
actionpack (5.1.4) lib/action_dispatch/routing/route_set.rb:43:in `controller'
actionpack (5.1.4) lib/action_dispatch/routing/route_set.rb:29:in `serve'
actionpack (5.1.4) lib/action_dispatch/journey/router.rb:50:in `block in serve'
actionpack (5.1.4) lib/action_dispatch/journey/router.rb:33:in `each'
actionpack (5.1.4) lib/action_dispatch/journey/router.rb:33:in `serve'
actionpack (5.1.4) lib/action_dispatch/routing/route_set.rb:834:in `call'
rack (2.0.3) lib/rack/etag.rb:25:in `call'
rack (2.0.3) lib/rack/conditional_get.rb:25:in `call'
rack (2.0.3) lib/rack/head.rb:12:in `call'
Try running rails g responders:install
Deleterack (2.0.3) lib/rack/session/abstract/id.rb:232:in `context'
ReplyDeleterack (2.0.3) lib/rack/session/abstract/id.rb:226:in `call'
actionpack (5.1.4) lib/action_dispatch/middleware/cookies.rb:613:in `call'
activerecord (5.1.4) lib/active_record/migration.rb:556:in `call'
actionpack (5.1.4) lib/action_dispatch/middleware/callbacks.rb:26:in `block in call'
activesupport (5.1.4) lib/active_support/callbacks.rb:97:in `run_callbacks'
actionpack (5.1.4) lib/action_dispatch/middleware/callbacks.rb:24:in `call'
actionpack (5.1.4) lib/action_dispatch/middleware/executor.rb:12:in `call'
actionpack (5.1.4) lib/action_dispatch/middleware/debug_exceptions.rb:59:in `call'
web-console (3.5.1) lib/web_console/middleware.rb:135:in `call_app'
web-console (3.5.1) lib/web_console/middleware.rb:20:in `block in call'
web-console (3.5.1) lib/web_console/middleware.rb:18:in `catch'
web-console (3.5.1) lib/web_console/middleware.rb:18:in `call'
actionpack (5.1.4) lib/action_dispatch/middleware/show_exceptions.rb:31:in `call'
railties (5.1.4) lib/rails/rack/logger.rb:36:in `call_app'
railties (5.1.4) lib/rails/rack/logger.rb:24:in `block in call'
activesupport (5.1.4) lib/active_support/tagged_logging.rb:69:in `block in tagged'
activesupport (5.1.4) lib/active_support/tagged_logging.rb:26:in `tagged'
activesupport (5.1.4) lib/active_support/tagged_logging.rb:69:in `tagged'
railties (5.1.4) lib/rails/rack/logger.rb:24:in `call'
sprockets-rails (3.2.1) lib/sprockets/rails/quiet_assets.rb:13:in `call'
actionpack (5.1.4) lib/action_dispatch/middleware/remote_ip.rb:79:in `call'
actionpack (5.1.4) lib/action_dispatch/middleware/request_id.rb:25:in `call'
rack (2.0.3) lib/rack/method_override.rb:22:in `call'
rack (2.0.3) lib/rack/runtime.rb:22:in `call'
activesupport (5.1.4) lib/active_support/cache/strategy/local_cache_middleware.rb:27:in `call'
actionpack (5.1.4) lib/action_dispatch/middleware/executor.rb:12:in `call'
actionpack (5.1.4) lib/action_dispatch/middleware/static.rb:125:in `call'
rack (2.0.3) lib/rack/sendfile.rb:111:in `call'
railties (5.1.4) lib/rails/engine.rb:522:in `call'
puma (3.10.0) lib/puma/configuration.rb:225:in `call'
puma (3.10.0) lib/puma/server.rb:605:in `handle_request'
puma (3.10.0) lib/puma/server.rb:437:in `process_client'
puma (3.10.0) lib/puma/server.rb:301:in `block in run'
puma (3.10.0) lib/puma/thread_pool.rb:120:in `call'
puma (3.10.0) lib/puma/thread_pool.rb:120:in `block in spawn_thread'
The below file is routes.rb
ReplyDeleteRails.application.routes.draw do
resources :serverdetails
root "serverdetails#index"
namespace :api, defaults: {format: :json} do
namespace :v1 do
resources :serverdetails, only: [:index, :create, :show, :update, :destroy]
# resources :microposts, only: [:index, :create, :show, :update, :destroy]
end
end
# mount API::Base, at: "/"
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
I changed the serverdetailscontroller.rb to the below one as in your code in the website..still the same error.
ReplyDeleteclass Api::V1::ServerdetailsController < ApplicationController
respond_to :json
def show
respond_with Serverdatail.find(params[:id])
end
# Creating users
def create
serverdetail=Serverdetail.new(serverdetail_params)
if serverdetail.save
render json: serverdetail, status: 201
else
render json: { errors: serverdetail.errors}, status: 422
end
end
# Updating users
def update
serverdetail = Serverdetail.find(params[:id])
if serverdetail.update(serverdetail_params)
render json: serverdetail, status: 200
else
render json: { errors: serverdetail.errors }, status: 422
end
end
private
def serverdetail_params
params.require(:serverdetail).permit(:servername, :cpu, :disk , :process)
end
end
How do I update existing values or create new values? The below commands are not causing any changes or creating any new values.
ReplyDeleteubuntu@rails-dev-box:/vagrant/r$ curl -X PUT -H '{serverdetails:{servername: "hexagon", cpu: "22", disk:"23", process:"55"}}' localhost:3000/api/v1/serverdetails/3
ubuntu@rails-dev-box:/vagrant/r$ curl -X POST -H '{serverdetails:{servername: "hexagon", cpu: "22", disk:"23", process:"55"}}' localhost:3000/api/v1/serverdetails/30
ubuntu@rails-dev-box:/vagrant/r$
ActiveRecord::Schema.define(version: 20171031041803) do
ReplyDeletecreate_table "serverdetails", force: :cascade do |t|
t.string "servername"
t.integer "cpu"
t.integer "disk"
t.integer "process"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
This comment has been removed by the author.
ReplyDeleteirb(main):002:0> Serverdetail.all
ReplyDeleteServerdetail Load (42.9ms) SELECT "serverdetails".* FROM "serverdetails" LIMIT ? [["LIMIT", 11]]
=> #, #, #, #]>
irb(main):003:0>
Is this correct?
ReplyDeleteRails.application.routes.draw do
resources :serverdetails
root "serverdetails#index"
namespace :api, defaults: {format: :json} do
namespace :v1 do
resources :serverdetails, only: [:index, :create, :show, :update, :destroy]
# resources :microposts, only: [:index, :create, :show, :update, :destroy]
get 'serverdetails/:id' => 'serverdetails#show'
patch 'serverdetails/:id' => 'serverdetails#update'
get 'serverdetails/' => 'serverdetails#index'
end
end
# mount API::Base, at: "/"
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
In PostmanI always get the error below:
ReplyDeleteThere was an error in evaluating the Pre-request Script: SyntaxError: Unexpected token :
when pasting this content:
'{"serverdetails":{"servername": "hexagon", "cpu": "22", "disk":"23", "process":"55"}}'
ubuntu@rails-dev-box:/vagrant/r$ rake routes
ReplyDeleteRubyDep: WARNING: Your Ruby has security vulnerabilities! (To disable warnings, set RUBY_DEP_GEM_SILENCE_WARNINGS=1)
RubyDep: WARNING: Your Ruby is: 2.2.3 (insecure). Recommendation: install 2.2.5 or 2.3.1. (Or, at least to 2.2.4 or 2.3.0)
Prefix Verb URI Pattern Controller#Action
serverdetails GET /serverdetails(.:format) serverdetails#index
POST /serverdetails(.:format) serverdetails#create
new_serverdetail GET /serverdetails/new(.:format) serverdetails#new
edit_serverdetail GET /serverdetails/:id/edit(.:format) serverdetails#edit
serverdetail GET /serverdetails/:id(.:format) serverdetails#show
PATCH /serverdetails/:id(.:format) serverdetails#update
PUT /serverdetails/:id(.:format) serverdetails#update
DELETE /serverdetails/:id(.:format) serverdetails#destroy
root GET / serverdetails#index
api_v1_serverdetails GET /api/v1/serverdetails(.:format) api/v1/serverdetails#index {:format=>:json}
POST /api/v1/serverdetails(.:format) api/v1/serverdetails#create {:format=>:json}
api_v1_serverdetail GET /api/v1/serverdetails/:id(.:format) api/v1/serverdetails#show {:format=>:json}
PATCH /api/v1/serverdetails/:id(.:format) api/v1/serverdetails#update {:format=>:json}
PUT /api/v1/serverdetails/:id(.:format) api/v1/serverdetails#update {:format=>:json}
DELETE /api/v1/serverdetails/:id(.:format) api/v1/serverdetails#destroy {:format=>:json}
api_v1 GET /api/v1/serverdetails/:id(.:format) api/v1/serverdetails#show {:format=>:json}
PATCH /api/v1/serverdetails/:id(.:format) api/v1/serverdetails#update {:format=>:json}
POST /api/v1/serverdetails(.:format) api/v1/serverdetails#create {:format=>:json}
GET /api/v1/serverdetails(.:format) api/v1/serverdetails#index {:format=>:json}
ubuntu@rails-dev-box:/vagrant/r$
Please note that only the api show action and api list action work. What changes should I make?
ReplyDeletein other words only the REST using GET are working..everything else fails
ReplyDeletePlease paste your code on https://pastebin.com as it is not readable in the comments. Also to get your problem solved you can reach our team at appychip[at]gmail[dot]com
DeleteI created a new app as api only and then added a view and it works. The remote linux server is now able to send data to the rails app using curl. But I need one more thing. When the cpu load goes above say 90 I need to set an alert. Where can the alert be set. Is it in the view or should I make a new model for that. Can you guide on the required code?
ReplyDeleteWhy are you doing so much of hardwork to get metrics. Why don't you use some free open source monitoring tool like sensu, nagios, prometheus etc.
DeleteI am in a learning process. I set a logic in the model which can send am email when the cpu is above a certain value.
ReplyDeleteReally nice blog post.provided a helpful information.I hope that you will post more updates like this Ruby on Rails Online Training Bangalore
ReplyDelete