Which is the right place to perform transformation on params?

Hi,

I have several scenarios where I need to do some transformations on the params of my POST request. This POST API is part of an API only application. These are the several use-case or scenarios where I need the transformation:

  1. Currently, the API does not make use of accepts_nested_attributes_for. API accepts a POST request with a nested structure in the body and explicitly calls save! on the parent object and then on the child object inside a transaction. I would like to make use of accepts_nested_attributes_for and hence I need to accept child_attributes key inside my params. Currently, I just have a key for children. Thus, I need to rename a key in the param. Hence, my questions are:
  • Is it worth using the accepts_nested_attributes for?
  • Where do I keep the param transformation code? Before the strong parameters permit or after?
  1. There are other scenarios where I deduce several keys based on a single key from the params. For eg. the API request sends a place_id and we make a call to the Google Places API to retrieve additional data based on the place_id and store it in the database. Thus, based on a place_id, we also store the location_name in the model. Where should such code reside which is populating location_name based on place_id?
  • Before the permit method in the controller?
  • After the permit method in the controller?
  • Inside the model by overriding the attribute setter for place_id?
  • Any other place?

Note that I also need to throw a 422 if the place_id is invalid.

Also, I cannot change the structure of the body in the incoming POST request since it is already used by a lot of applications.

I think the controller is where any incoming params should be permitted then processed for use elsewhere in the app. I tend to do this:

class SomeController < ApplicationController
  def some_action
    model = SomeModel.new(some_model_params)
    ...
  end

  private

  def some_model_params
    base = params.require(:some_model).
      permit(:place_id, children: [:child_attribute])
    
    base[:location_name] = google_places_api(base[:place_id])
    base[:child_attributes] = base.delete(:children)
    
    base
  end
end

You could also have a separate SomeModelParams class to mung the params to your liking.