I am building web API with Grape gem on Ruby on Rails 4.1, with their 'version' function.
Sample code here.
# app/api/api.rb
class API < Grape::API
prefix 'api'
format :json
formatter :json, Grape::Formatter::Rabl
default_format :json
mount V1::Root
end
# app/api/v1/root.rb
module V1
class Root < Grape::API
version 'v1'
resource :users, rabl: "users" do
get '/' do
#users = User.all
end
end
end
end
# config/routes.rb
mount API => "/"
with these code, app/views/api/users.rabl is used for view template on request to http://localhost:3000/api/v1/users.
I want to use template in app/views/api/v1 for v1 request. Is there any way to do that ?
current
/api/v1/users -> app/views/api/users.rabl
/api/v2/users -> app/views/api/users.rabl
want
/api/v1/users -> app/views/api/v1/users.rabl
/api/v2/users -> app/views/api/v2/users.rabl
I'm using Grape Entity:https://github.com/intridea/grape-entity
So I created a directory on v1 folder called entities
ex:
api/v1/entities/token_response_entity.rb
module ExampleAPI::V1::Entities
class TokenResponseEntity < Grape::Entity
expose :token , documentation: { type: 'string', desc: 'Token String' }
end
end
So when I need to present I just need to use:
present tokens, with: ExampleAPI::V1::Entities::TokenResponseEntity
I finally took Jan's way.
resource :users, rabl: "v1/users" do
Related
I am following this article to learn about writing api using grape gem. But in the response I am not getting the root key. Here is my directory structure,
app
|––controllers
|––api
|––base.rb
|––v1
|––base.rb
|––graduates.rb
In app/controllers/api/v1/graduates.rb:
module API
module V1
class Graduates < Grape::API
include API::V1::Defaults
resource :graduates do
desc "Return all graduates"
get "", root: :graduates do
Graduate.all
end
desc "Return a graduate"
params do
requires :id, type: String, desc: "ID of the
graduate"
end
get ":id", root: "graduate" do
Graduate.where(id: permitted_params[:id]).first!
end
end
end
end
end
in app/controllers/api/v1/defaults.rb
module API
module V1
module Defaults
extend ActiveSupport::Concern
included do
prefix "api"
version "v1", using: :path
default_format :json
format :json
formatter :json,
Grape::Formatter::ActiveModelSerializers
helpers do
def permitted_params
#permitted_params ||= declared(params,
include_missing: false)
end
def logger
Rails.logger
end
end
rescue_from ActiveRecord::RecordNotFound do |e|
error_response(message: e.message, status: 404)
end
rescue_from ActiveRecord::RecordInvalid do |e|
error_response(message: e.message, status: 422)
end
end
end
end
end
I have used grape-active_model_serializers gem to serialize like the following,
In app/serializers/graduate_serializer.rb
class GraduateSerializer < ActiveModel::Serializer
attributes :id, :name
end
I got the following output. [{"id":1,"name":"aaaa"},{"id":2,"name":"bbbb"},{"id":3,"name":"cccc"},{"id":4,"name":"dddd"}]
But As I added get "", root: :graduates do in app/controllers/api/v1/graduates.rb file, I am expecting the following output,
{"graduates": [{"id":1,"name":"aaaa"},{"id":2,"name":"bbbb"},{"id":3,"name":"cccc"},{"id":4,"name":"dddd"}]}
What I am missing. why graduates is not added as root. Help me to fix this issue.
Dirty fix: use in app/controllers/api/v1/graduates.rb
get "" do
{ graduates: Graduate.all }
end
Or you can delete gem grape-active_model_serializers, delete class GraduateSerializer, delete row formatter :json, Grape::Formatter::ActiveModelSerializers from defaults.rb, and add gem grape-entity to Gemfile, install it, add code to app/app/entities/graduate_entity.rb:
class GraduateEntity < Grape::Entity
root 'graduates', 'graduate'
expose :id
expose :name
end
Change code in app/app/controllers/api/v1/graduates.rb:
get "" do
present Graduate.all, with: GraduateEntity
end
I was struggling with similar issue as you, I resolved it by adding
ActiveModelSerializers.config.adapter = :json
in config/initializers/serializer.rb
Let me know if it could help you
I have created a rails project that has some code that I would like to execute as an API. I am using the rails-api gem.
The file is located in app/controllers/api/stats.rb.
I would like to be able to execute that script and return json output by visiting a link such as this - http://sampleapi.com/stats/?location=USA?state=Florida.
How should I configure my project so that when I visit that link it runs my code?
the file should be called stats_controller.rb app/controllers/api/stats_controller.rb
you can create an index method where you can add your code
class API::StatsController < ApplicationController
def index
#your code here
render json: your_result
end
end
in the file config/routes.rb you should add
get 'stats' => 'api/stats#index', as: 'stats'
To access the params in the url you can do it in your index method with params[:location] ,params[:state]
Here's how I would think of this:
in app/controllers/api/stats_controller.rb
module Api
class StatsController
def index
# your code implementation
# you can also fetch/filter your query strings here params[:location] or params[:state]
render json: result # dependent on if you have a view
end
end
end
in config/routes.rb
# the path option changes the path from `/api` to `/` so in this case instead of /api/stats you get /stats
namespace :api, path: '/', defaults: { format: :json } do
resources :stats, only: [:index] # or other actions that should be allowed here
end
Let me know if this works
I just started using Rails for my web application's framework. Now I'm working on an API for a test project. I want the key of my API's JSON response to be camelcase instead of snakecase. How can I do that?
My JSON respond is something like this:
{"id":3,"name":"Bingo Gem", ..., "can_purchase":true,"shine":6,"created_at":"2014-07-31T06:34:37.917Z","updated_at":"2014-07-31T06:34:37.917Z","reviews":[]}
The response I expected:
{"id":3,"name":"Bingo Gem", ..., "canPurchase":true,"shine":6,"createdAt":"2014-07-31T06:34:37.917Z","updatedAt":"2014-07-31T06:34:37.917Z","reviews":[]}
Here's one of my controllers:
module Api
module V1
class PermataController < ApplicationController
respond_to :json
def index
respond_with Permatum.all, :include => [:reviews]
end
end
end
end
and routes.rb:
namespace :api, defaults: { format: 'json'} do
namespace :v1 do
resources :schedules
resources :permata
end
end
In Rails, there is a method camelize in String class. Use it:
"can_purchase".camelize(:lower) #=> "canPurchase"
"created_at".camelize(:lower) #=> "createdAt"
I created a practice rails app where I have created a namespace and versioned like is demonstrated in this railscast. Everything is working fine and I can see the json output in the browser
Then I added the Rabl gem and was trying to render the rabl views but I get an empty JSON array rendered in the browser
Here is what I did systematically to get the versioning working
1) changed the routes file to look like this
App0828::Application.routes.draw do
namespace :api, defaults: { format: 'json'} do
namespace :v1 do
resources :vendors
end
end
#resources :vendors
#root to: 'vendors#index'
end
2) The created thise files
app/copntrollers/api/v1/vendors_controller.rb
Inside the vendors_controller.rb I added the following code
module Api
module V1
class VendorsController < ApplicationController
class Vendor < ::Vendor
#subclass vendor class so thatyou can extend its behaviour just for this version
#add any functions specific to this version here
end
respond_to :json
def index
respond_with Vendor.all
end
def show
respond_with Vendor.find(params[:id])
end
..........
3) Then I pointed my browser to this url "http://localhost:3000/api/v1/vendors"
And I can see the json output in my browser
4) Then I added the rabl gem
5) restarted the server
6) Changed the above file at app/copntrollers/api/v1/vendors_controller.rb
from the version above to the version below
module Api
module V1
class VendorsController < ApplicationController
class Vendor < ::Vendor
#subclass vendor class so thatyou can extend its behaviour just for this version
#add any functions specific to this version here
end
respond_to :json
def index
render 'api/v1/index.json.rabl'
end
def show
render 'api/v1/show.json.rabl'
end
.......
7) I created the following files with this code:
file: app/views/api/v1/show.json.rabl
code: object #vendor
attributes :id, :name
file: app/views/api/v1/index.json.rabl
code: object #vendors
attributes :id, :name
8) Routes file looks like this
api_v1_vendors GET /api/v1/vendors(.:format) api/v1/vendors#index {:format=>"json"}
POST /api/v1/vendors(.:format) api/v1/vendors#create {:format=>"json"}
new_api_v1_vendor GET /api/v1/vendors/new(.:format) api/v1/vendors#new {:format=>"json"}
edit_api_v1_vendor GET /api/v1/vendors/:id/edit(.:format) api/v1/vendors#edit {:format=>"json"}
api_v1_vendor GET /api/v1/vendors/:id(.:format) api/v1/vendors#show {:format=>"json"}
PUT /api/v1/vendors/:id(.:format) api/v1/vendors#update {:format=>"json"}
DELETE /api/v1/vendors/:id(.:format) api/v1/vendors#destroy {:format=>"json"}
9) Finally I went to url: "http://localhost:3000/api/v1/vendors.json"
And all I see in the browser is an empty JSON hash: "{}"
So clearly it cannot access the instance variables. Seems to be some issue with being out of scope. Im not sure how to proceed next. I couldn't find any examples online of versioning with rabl. Any suggestions? Id really appreciate it
Thanks
Where are you setting the instance variables?
First, you probably should have this line: respond_to :json in your Api::ApplicationController (or something like that, basically the controller from which every other controller in the Api module inherit).
You dont need these: render 'api/v1/index.json.rabl'.
Just set your instance variables:
def index
#vendors = Vendor.all
respond_with #vendors
end
def show
#vendor = Vendor.find(params[:id])
respond_with #vendor
end
I'm developing an application that is primarily an API gateway. In the expectation that we'll be developing multiple versions of the API over time and with the interest of having backward compatibility, I am looking to have something along the lines of:
http://host.domain.com/apiv1/:token/:module(/:id(:method))
Given this, what I am looking to do is to have a sub-routing system of my own within each API. What I'd like to have in terms of a file structure in the Controller directory is something akin to the following:
/app/controllers/apiv1_controller.rb
/app/controllers/apiv1/module_controller.rb
/app/controllers/apiv1/response_controller.rb
and eventually also have:
/app/controllers/apiv2_controller.rb
/app/controllers/apiv2/module_controller.rb
/app/controllers/apiv2/response_controller.rb
What this breaks down to is that I am unsure how to call methods within the controllers in the subdirectories, using something like:
return Apiv1::ResponseController.index
gives me:
undefined method `index' for Apiv1::ResponseController:Class
Any leads? Does this setup require that I explicitly "require" the requisite file manually?
Pasted Here in response to the question:
routes.rb
AppName::Application.routes.draw do
resources :users
match 'api-v1/:token/:module(/:id(/:method))' => 'apiv1#route'
root :to => "welcome#index"
end
apiv1_controller.rb
class Apiv1Controller < ApplicationController
protect_from_forgery
respond_to :json
def route
Rails.logger.level = 0
logger.info("ROUTE ACTION")
logger.info("params: #{params}")
Apiv1::ResponseController.index(params)
end
end
apiv1/response_controller.rb
class Apiv1::ResponseController < ApplicationController
protect_from_forgery
respond_to :json
def index(params)
Rails.logger.level = 0
logger.info("INDEX ACTION")
result = {
'success' => true,
'controller' => 'response',
'api' => 'v1'
}
render :json => result
end
end
If you are looking for versioning your REST APIs, you can use the restful_route_version gem. It also takes care of inheriting one version from another so that you don't rewrite the same REST APIs for every version.