I am writing my first "real" Rails app after making a few successful learning projects and I'm still a little bit iffy on best practises for App vs API routes. Ideally, API routes will be RESTful, mainly revolving around using resources :articles or whatever, but I'm wondering which of the the following two scenarios is best
Create the app itself using RESTful routes, then bolt on an API in the "api" namespace/subdomain for example, and after authentication, piggy back off the same routes (not sure exactly how to do the piggy backing yet with different authentication, but that will come later. Or;
Create the app itself with whatever routes are semantic and work well (still ideally CRUD based), then create an entirely new set of controllers and routes for the API (RESTful)
I appreciate any feedback that anyone has.
The default "Rails way" is to make your models available to different clients through a single set of URIs, handled by a single set of controllers — /articles for browsers, /articles.json or /articles.xml for programmatic access. You can see that in Rails' emphasis on "resources", the respond_to mechanism in your controllers and so on.
This often works very well for simpler APIs that match the CRUD semantics closely. If yours for whatever reason does not, you might be better off with separating it out.
Try starting out the simple way by sticking to this 'one controller for both' approach. If you like, you could use
scope '(api)' do
resources :articles
resources :authors
resources :comments
# ...
end
to add an optional '/api/' path segment to your URLs - in this case, giving you both /api/articles and /articles, routed to the same controller. This gives you an easy way out, should you later decide to switch to separate controllers for HTML and API access.
Related
I have a decent amount of experience with rails, but I've always been a bit ad hoc with my development methods. I'm curious about how to be properly RESTful in rails. Here's an example of an app I'm working on now:
I have a few models, including a User, Pack, and Product model. The models each have a controller associated with them. If I want to create a new page called 'Dashboard', on which the User can create new records on the Pack model, as well as see their account information, how do I do this in a restful way? Do I create a new controller called Dashboard? Or do I add it to a controller that defines my 'Static Pages'? What's the best practice regarding pages that aren't exclusive to the actions on one model?
Thank you in advance!
Yes, basically you want one controller per resource. In this case the resource is a combination of things but since you've identified a singular meta-resource (a "dashboard") it makes sense. So, I'd create a DashboardsController and then have a route like:
resource :dashboard, only: :show
Then you can use dashboard_url for links to the dashboard.
NOTES: The singular resource in the routes file is important because it indicates you don't have a list of resources, just a single one. This means there won't be an index action and the show action will be the default -- thus dashboard_url doesn't require a resource to show to be passed to it. And, regardless, controllers are named in the plural -- thus DashboardsController.
I would create a DashboardsController despite not having Dashboard model. Then to maintain REST principles have a controller method :new and controller method :create. Simply disply the information on the dashboards/new.html.erb but have the form post to the :create controller action.
To supplement the answers already given, REST really exists to make an easily-consumable API. (Notably, Roy Fielding's work is in the area of APIs, and REST is built on top of HTTP methods that HTML doesn't allow.)
This has been a common stumbling block since RESTful routing debuted in Rails; people get the impression that every controller now needs to be RESTful and that the front end and API should never diverge. Frequently, though, the front end and API have different needs.
For resources you want to be accessible via an API—Products, Users, Packs—trying to be RESTful is ideal. For those sorts of uses, pdobb's advice to have one controller per resource is generally exactly what you're looking for. They make sense in the context of an API being consumed by a machine.
But what about the dashboard? Dashboards are generally an HTML view intended to be rendered in a browser and viewed by a human being.
Does a "dashboard" resource make sense in the context of an API? Would it make more sense to instead make that data available in other resources? Is that data available in other resources?
Even if a "meta-resource" aggregating data still makes sense, would it be more logical if the API version had another name, like AccountSummary?
At the moment I'm implementing versioning for our REST API in our Rails application. Is there a way this can be implemented so you only define new functionality in new versions? Say for example:
I have a Users controller and a Products controller. Products has stayed the same between V1 and V2, but Users has changed. It would be nice if we could set it up so that if I call for V2 of Products, my application knows it doesn't exist and simply uses the most recent controller version instead (in this case, V1). This would allow for us to not create a whole new set of controllers for every version; we'd only create new controllers for the specific functionality that has changed.
Below is a snippet from our Routes.rb, and admittedly I have no idea how to tackle this problem. Any help would be greatly appreciated!
namespace :api do
scope module: :v1, constraints: ApiConstraints.new(version: 1) do
resources :users
resources :products
end
scope module: :v2, constraints: ApiConstraints.new(version: 2) do
resources :users
end
end
I've used the same strategy for sometime and recently changed approaches-and admittedly I'm biased because I'm also one of the authors of our new approach with VersionCake.
This approach is to only version our payloads or views, afterall views are the contract between the client and if there is a breaking contract change, then the version needs to change. In VersionCake, you can version your views like so:
app/views/products/show.v1.rabl
app/views/products/show.v2.rabl
app/views/users/show.v1.rabl
The specific feature that is important based on your question is that VersionCake will gracefully degrade to the latest supported version. Given the example above, a client requesting users/1.json for version 2 will receive the payload for users/show.v1.rabl.
All this being said, it would require adopting a new versioning approach, so it might not be the best answer, but I think it's an option worth considering.
Have you looked at Grape already?
What I have done over the API's I have built is, when something changes for all of them (like authentication) I just get them all to inherit from a specific versioned api controller, as in Api::V1::BaseController and place the common behavior and controls in there.
Since changing the API usually also meant changing the representations, using a tool that generates the representations for your is also really important, there are many gems out there for this as:
RABL
Roar
Jbuilder
This is what Grape is designed to do.
In addition, you have the option of mounting your API in your routes.rb file or mapping it in config.ru, bypassing the Rails stack and greatly improving the performance of the API.
The default route file has the following segment:
# This is a legacy wild controller route that's not recommended for RESTful applications.
# Note: This route will make all actions in every controller accessible via GET requests.
# match ':controller(/:action(/:id))(.:format)'
So what exactly is the problem with letting Rails assume the controller, action, and view to display when the format is ':controller(/:action(/:id))(.:format)' ? I mean, for more specific things like nesting I can always use specific routes...
It's just a common-sense security measure.
It forces the designer to whitelist GET requests so that the app does not inadvertently divulge the contents of a table or record to a malicious user.
If developers are allowed to being lazy, they will be. We are super great at optimizing efforts.
Not being explicit about routes may help you save 5 seconds each time you have a new controller, but it will be a headache for new developers, which will be unfamiliar with your codebase and hence don't know how things are glued. They will have to reverse engineer your views-controllers.
Being there. Not pretty.
Being explicit is usually better.
Because not everyone is building the next Facebook and some of us do this for fun or to make simple apps that we use only on our development device or to simply not be a ass and "answer" a question with that actually has an answer, instead of make ourselves look uber smart and important:
match '/:controller(/:action(/:id))(.:format)', to: "#{:controller}#{:action}", via: [:get, :post]
In the process of making an ebook, users have to edit its content, edit its metadata, choose marketing options, (such as pricing and distribution) and publish.
I started to put each everything inside the Book resource (contents, metadata, marketing options etc)
Then each step of the process (contents, metadata, marketing) is an action inside BooksController.
But Books started to grow a lot in terms of attributes and actions.
I'm wondering if it is better to force RESTfulness by creating singleton resources, each associated to their respective book.
The routes would be:
/book/12/content/edit
/book/12/metadata/edit
/book/12/marketing/edit
This seems more elegant, as each of these "resources" are RESTful and have few attributes each. But the resources are not objects (Marketings?).
Do you thing it's OK to create meaningless resources just to be RESTful and keep code in order?
Is there a better way to group similar attributes than create resources out of them? Thanks.
I'm porting a large PHP application to Rails and we have a fair few "ad-hoc"resources, such as admins have the ability to log in as another user with their permission, in order to correct issues etc. I would always complain that features such as logging in as other users should not be bundled into a huge monolithic AdminController as actions loginAsUser and logOutOfUserAccount. In our Rails app we try to visualize as much as we can in terms of resources, so using the example I just gave, we have an Admin namespace, under which there is a UsersController (/admin/users/:id) and as a sub-resource of users, we have a UserOverride resource (/admin/users/:user_id/override)... it feels really logical that we just POST and DELETE to this resource.
So getting back to your example, yes, I think you should break those sections down into separate resources. It certainly seems like BookContent should be a sub-resource of Book, and MarketingOptions too.
resources :books do
resource :content
resources :marketing_options
end
etc
This is both nicer to work with (more modular) and easier to visualize just from looking at the routes alone. You also get the benefit of really predictable path helpers:
<%= link_to("Marketing Options", book_marketing_options_path(#book)) %>
You can't try to force every conceivable route into a RESTful resource ideology though... if it feels like it's forced, then it probably is.
There's a decent blog post I wanted to link to that (despite some poor grammar) actually does a pretty good job of showing you how to think about your application in terms of resources... I can't find it though. I'll add a comment if/when I do.
EDIT | Just re-reading your original question and wanted to clarify: resource != row in database. A resource is anything you can conceive as a "thing"... I know that's a very broad statement, but just like an Object in OOP doesn't have to represent something concrete/material, nor does a resource in a RESTful design.
I'm pretty new to Rails 3 and I'm trying to understand the advantage of designing an application in a RESTful way. I don't need an API/Web Service. Don't need XML or JSON.
The application I'm building uses no CRUD at all. It is an app that gathers trade data via a socket connection and displays it to a user in many different ways.
I would like to visualize trades in different ways such as:
Most recent
Highest Yielding
Trades by State
Most active trades
Million dollar trades
Trades that are general obligation bonds
etc…
In the "Rails way" it seems that I would have a very overloaded index action. Or I could go against convention and just create methods in the trades controller like most_recent, highest_yielding, most_active, etc. But that seems to go against the whole philosophy of designing an app in Rails 3.
It just seems like the idea behind a RESTful approach in Rails is based around CRUD and falls short when CRUD isn't involved. Is there really an advantage to designing your app to be "RESTful" besides following a convention? I'm not really seeing the advantage here.
Plus, if I ever do need an API, I would imagine that it would be much better to design an API with an API in mind. My API wouldn't be a direct 1 to 1 match of my website which is built for human consumption vs a machine.
I'd appreciate any insight on this. Maybe I'm missing something here?
CRUD is actually involved when resources are involved. And since virtually every application has resources CRUD can be involved and is usually(if you ask me), the best way to do it.
The idea is that a resource has certain actions. You view a resource, you edit it, you delete it and some more. The methods like most_recent(or scope for this one) should be used in models and not controllers. Then, if you need to use that collection, you would just call something like :
#recent_posts = Post.most_recent
in your controller. Your controllers should not have much code, actually no business logic at all.
RESTful is very nice because it handles resources naturally. A controller should actually be handling a resource. If you think that something can be edited or created, then it should be handled by a controller.
Generally, i highly suggest that you take a deeper look and you will definitely see the advantages on your own.
I have idea how to do it. Code is not tested, I just wrote it without running.
Controller:
# trades_controller.rb
def index
# all scopes defined in model will be allowed here
# not good idea if you don't want it
if Trade.scopes.has_key?(params[:scope].to_sym)
#trades = Trade.send(:params[:scope])
else
# render error or what you want
end
end
Model
# trade.rb
scope :most_recent, order(:created_at)
# more scopes
View
# index.html.erb
link_to 'Most recent', trades_path(:scope => 'most_recent')
Your design should always take into account the requirements of your application first and the philosophy of the framework second.
If the philosophy doesn't fit your requirements or what you believe is the best way to develop your application then either ignore it, or, if the framework makes it too difficult for you to build like you think you should (which isn't the case in Rails imho), switch to another framework.
All of that doesn't have much to do with REST. For more info on why REST is considered a good idea (for some, not all, things), see the following SO Q&A's: Why would one use REST instead of Web services and What exactly is RESTful programming.