Create basic RESTful API Server to a datastore - ruby-on-rails

I have the most basic of questions, but the more I think about it, the more complex it gets.
I've been using rails and it follows the MVC paradigm in that db and api calls are abstracted through calls generated through the controller. This seems way too heavy for what I want.
1) I want a simple (basic) web server that sits in front of my datastore. (The contents of which happen to be stored in a directory structure that follows: /LOCATIONS/LOCATION/PRESENTERS/PRESENTER/YEAR/MN/)
2) I want to be able to host json files within that directory structure and GET them as needed.
3) I want to be able to PUT/POST append to those json files.
Seems like all I'd need is nginx with my datastore as a doc root and index.html files at critical places within the structure (e.g. site.com/Locations/index.html , site.com/locations/SF/presenters/solomon/index.html)?
How would I begin to solve this problem, (without the use of controllers of coarse)?

MVC Frameworks
without the use of controllers
You must be aware that there are many more frameworks than Rails out there, so when you ask about using a system to "sit in front of your datastore", you're really looking for different frameworks to handle requests, of which there are many.
The problem you have is how do you keep data consistency, whilst ensuring you can handle the relevant API requests (through JSON). The answer is to look at how the systems you're examining work.
I can only really vouch for Rails (it's the only framework I've got production apps for) -
--
Rails
Creating an API in Rails is so simple - I don't know why you'd think about doing anything else
Using the MVC principle might seem bloated to you, but the security, structure and extensibility it provides is unmatched.
Here is how to create an API in Rails:
#config/routes.rb
namespace :api do
resources :controller, only: [:update, :create] #-> only PUT / POST
end
#app/controllers/api/your_controller.rb
class API::YourController < ApplicationController
respond_to :json
def update
# handle PUT request
end
def create
# handle POST request
end
end
#app/models/model.rb
Class Model < ActiveRecord::Base
end
This is all you need - you'll be able to access domain.com/api/controller.json POST to create data, and domain.com/api/controller/4.json PUT to update it :)

Related

API docs for a generic controller that handles all requests

I need to generate api endpoints for certain database views we have in our postgres database dynamically, as we may add / delete views we don't want to update the code every time we do this.
for this i have a generic controller which handles all requests and I create model class on the fly based on which view needs to be accessed.
when a request is sent to the generic_api#index based on the end_point passed i create certain model classes on the fly and query them.
routes.rb
namespace 'api' do
namespace 'v2' do
get '*end_point', to: 'generic_api#index'
end
end
const = ClassFactory.class_object(end_point.classify, result)
Octopus.using(result['shrad'].to_sym) do
result = const.ransack(params[:q]).result.page(params[:page]).per(10000)
render json: result.to_json
end
The api itself is working as expected, however i am not sure what's a good way to generate documentation for the API since i only have one controller. we were earlier using the apipie gem but the documentation seems to be tightly coupled with controllers for each end point.
Any help on how to generate api documentation when I have a single controller handling multiple end point requests would be great, Thanks.

Where would be a good place to store logic for a call to a third party api, based on Ruby on Rails best practices?

I am practicing making third-party api calls using the rest-client gem. Right now, my code for making a call to a third-party api is stored in a show method in my search_controller.rb. I'm assuming it would be better to store that logic somewhere other than a controller, in order to follow the "skinny controller" practice of ruby on rails. Where would be a better place to put this type of logic, or is this an ok place to have it?
I would say two different kinds of objects in conjunction.
Clients
These are classes that simply do HTTP calls and touch the application boundary. The reason you want to isolate this in a discrete object is that it allows you mock it in tests. It also keeps the responsibilities clear.
# adapted from the HTTParty readme
class StackExchangeClient
include HTTParty
base_uri 'api.stackexchange.com'
def initialize(service, page)
#options = { query: { site: service, page: page } }
end
def questions
self.class.get("/2.2/questions")
end
def users
self.class.get("/2.2/users")
end
end
/lib is a pretty good place to store clients since they rarely should contain any application specific logic.
Service objects
Service objects are Plain Old Ruby Objects (PORO) that are designed to
execute one single action in your domain logic and do it well.
-- Rails Service Objects: A Comprehensive Guide
class QuestionImporterService
def initialize(service, page, client: nil)
#client = client || StackExchangeClient.new(service, page)
end
def call
questions = #client.get_questions
questions.map do |attributes|
Question.new(attributes)
end
end
end
This example uses constructor injection to allow you to mock out the HTTP calls for testing.
You can place services in /app/services. Sometimes Spring gets "stuck" and does not pickup new folders in /app for autoloading in which case you restart it by calling $ spring stop.

GraphQL Ruby Using NameSpace

I am using graphql-ruby in my rails application.
Currently, my app's directory structure looks like this.
app
- controllers
- application_controller.rb
- graphql_controller.rb
- admin
- application_controller.rb
- graphql_controller.rb
- graphql
- types
- mutations
- graphql_schema.rb
I am trying to make a simple admin page but I'm not sure how I should hanlde namespaces with graphql-ruby.
Should I make Admin directory under graphql as well and make types and mutations under it for the data I want to use on the admin page??
Also, should I make another endpoint for Admin like the code below??
Rails.application.routes.draw do
namespace :admin do
post :graphql, to: 'graphql#execute'
end
post :graphql, to: 'graphql#execute'
end
Can you possibly give me the link of a project that does what I am trying to do with graphql-ruby??? That would be a tremendous help.
From https://graphql.org/
GraphQL APIs are organized in terms of types and fields, not endpoints. Access the full capabilities of your data from a single endpoint.
Hence, creating two endpoints as you have suggested would go against that principle. You probably shouldn't do it, but most importantly, there's no need to.
Suppose you have a type ProductType with a couple of fields. You can use that same type to both query/display the product data in your website and edit it with a mutation in the admin page. Granted, you may have to deal with authorizing some specific queries and mutations, but it shouldn't be any harder than dealing with authorization in REST.
See more about GraphQL authorization in Ruby.

How to create a scaffold with differing controller and model names?

I'm making a versioned JSON API in rails, where the controllers also respond to HTML, meaning it can be accessed as a browser or through an app I'm developing. The controllers have the form Model::V1::UsersController (Model instead of API since they don't just respond to JSON), and I currently have the following in my routes.rb:
namespace :model, path: 'm', as: '' do
# For objects in the model, accessible by JSON (through the app) or HTML (through the browser, using forms to send data to the server).
scope module: 'v1', constraints: OrConstraint.new([APIConstraint.new(1), APIConstraint.new(:default)]) do
resources :users do
collection do
post :sign_in
end
end
end
end
I plan to add more models to my API, but how can I use scaffolding to do this? For example, to create a controller Model::V1::CommentsController, but using the Comment model, instead of Model::V1::Comments.
I've been trying to figure this out for hours, and googling for people with similar problems shows that a few people say not to use scaffolding at all in this case: I don't want to do this, as it would mean writing all the views myself, which would be very time-consuming. Apart from that, I can't find much. nifty-generators was suggested somewhere, but it doesn't seem to be maintained anymore: no activity since 2012. I'm new to rails, and it might be that I've missed something quite obvious, but I find it surprising that not many others have had the same issue.
I've considered making my own generator, but looking at the source of https://github.com/rails/rails/blob/master/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb, it seems very complicated.
EDIT: I've just discovered that I can pass the --model-name parameter to the rails scaffold generator to achieve what I want, but for some reason it still tries to create a model with the same name as the controller. How can I change this?
I've settled with this solution, by not generating a model at all using the scaffold generator:
To create Model::V1::CommentsController as the controller and Comments as the model:
rails g model comment
rails g scaffold model/v1/comments --model-name=comment --no-orm

Encrypting Parameters across controllers

I need to pass a parameter from one method in a controller to another.
From my understanding I have to pass the parameters as a GET exposing it in the url string.
What is the best way to encrypt the data so no one can see what is actually getting passed in the string? Also, is there a way to pass it via POST or is my original understanding correct?
I haven't used RoR, but in the web world, this problem is solved with sessions. Using sessions you can store the parameters on the server and avoid sending sensitive data with GET or POST (both are insecure).
The Ruby on Rails Security Guide looks like a great read related to this.
I suggest you abstract your code into lib/ so that you don't have to call additional methods. Instead of making a new HTTP request, just put the code in a central place and call it from there.
class MyController < ApplicationController
def index
MyLibrary::Thing.do_stuff
end
def show
MyLibrary::Thing.do_stuff
end
end
# lib/my_library/thing.rb
module MyLibrary
module Thing
def self.do_stuff
# do stuff!
end
end
end
That way you can access the same code in multiple actions, without doing extra HTTP requests.

Resources