Thoughts on adding non-standard REST actions to a resource?
I have a
milestone resouce that can be activated/deactivated as well as completed/reopened.
Rails pushes the standard 7 actions in your controllers: index, new, create, show, edit, update, destroy.
These 7 actions work great for most things, but my use case didn't strictly fit into those 7 REST actions. I read an article a while back that some respected Rails developers follow REST conventions by creating resources such as:
- activate => POST /milestones/:id/activations
- deactivate => DELETE /milestones/:id/activations
- complete => POST /milestones/:id/completions
- reopen => DELETE /milestones/:id/completions
I used this approach for a while but I've found it to be difficult to work with.
It adds additional files, which sometimes leads to more complexity since there is now more to manage.
The biggest problem I encountered was it didn't make logical sense that the reopening of a milestone record was at the endpoint
DELETE /milestones/:id/activations. It made more logical sense to me that it would be
PUT /milestones/:id/reopen, since it is something we are doing to the
I've been contemplating moving these non-standard actions to the
milestones_controller.rb file itself and updating my routes accordingly.
I wanted to get some thoughts on these 2 different approaches and see how others had solved this problem of custom actions on resources?
I think it's fine for you to make some non-standard actions here.
Are these actions changing the Milestone record or are they change an Activation / Completion records associated to a Milestone?
These actions mostly touch the
milestone model, but they update children associations (ie, when a
milestone is activated, it's children
task records are activated as well).
There is no separate Activation or Completion record, those were just names I gave to the actions I was taking on a
I also found that Github and Stripe both seem to use custom actions, so it made me feel a little better about moving to that approach.
Specifically with activate/deactivate, I have 10+ separate resources it can apply to, and it was much easier for me to remove those 10 separate controller files and just put the methods on the already-existing controllers.
It's always good to get input from folks in the community I look to for helping establish "best practices"!
So what I would probably do here is actually:
resources :milestone do member do post :activate, to: "milestones/activations#create" delete :deactivate, to: "milestones/activations#destroy" end end
Then you can have a
app/controllers/milestones/activations_controller.rb that handles them. That's great for separating out the logic from the main CRUD, and your routes aren't too complicated.
I'll often make a singular resource for things like this too.
resources :milestone do scope module: :milestones do resource :activate end end
Works a bit better if you're doing like
resource :advanced_search or something.
In your case,
DELETE /milestones/1/activate doesn't make sense, and you'd want the destroy route to be
DELETE /milestones/1/deactivate so it's more intuitive. And that's why I'd probably write the custom routes instead of resources in this case.
I didn't even though about using the custom route names but then pointing them to separate controllers to keep the code clean. I got too stuck in the "all or none" mindset of only doing it one way.
Thanks for the input!