ruby-on-railsjsonapifastjsonapi

Is there a way to select just one attribute of an outer object by using the Fast JSON API in Rails?


I am creating a travel app which uses a backend Rails API. I've decided to use the Fast JSON API to serialize my data. I have a list of countries; each country has many cities, and each city has many attractions.

These are a list of my associations between my models.

Country.rb 
has_many :cities
has_many :attractions, through: :locality

Localities.rb
has_many :attractions
belongs_to :country

Attraction.rb
belongs_to :locality

When I serialize my data for an individual attraction, I would like to include only the name attribute of the city and the name attribute of the country it belongs to. I am currently doing this by adding the optional parameter to include the locality and country name.

  def show
    attraction = Attraction.find_by(slug: params[:slug])
    options = {}
    options[:include] = [:locality, :'locality.country.name']
    render json: AttractionSerializer.new(attraction, options).serialized_json
  end

However, this gives all the attributes and relationships of the country, including a list of all unrelated localities nested within the country, which will become really inefficient when my dataset becomes larger. See below:

{
"data": {
    "id": "1",
    "type": "attraction",
    "attributes": {
        "name": "Plaza de España",
        "description": "Site of the World Exposition in 1929",
        "types": null,
        "latitude": 40.4232824,
        "longitude": -3.7107257,
        "slug": "plaza-de-espana",
        "locality": {
            "id": 1,
            "name": "Seville",
            "country_id": 168,
            "created_at": "2020-06-10T05:43:47.474Z",
            "updated_at": "2020-06-10T05:43:47.474Z",
            "slug": "seville",
            "latitude": 37.3886303,
            "longitude": -5.9953403
        }
    },
    "relationships": {
        "locality": {
            "data": {
                "id": "1",
                "type": "locality"
            }
        }
    }
},
"included": [
    {
        "id": "1",
        "type": "locality",
        "attributes": {
            "name": "Seville",
            "latitude": 37.3886303,
            "longitude": -5.9953403,
            "slug": "seville"
        },
        "relationships": {
            "country": {
                "data": {
                    "id": "168",
                    "type": "country"
                }
            }
        }
    },
    {
        "id": "168",
        "type": "country",
        "attributes": {
            "name": "Spain",
            "slug": "spain",
            "iso_3166_1_alpha2": "ES",
            "iso_3166_1_alpha3": "ESP",
            "iso_3166_1_numeric": "724"
        },
        "relationships": {
            "localities": {
                "data": [
                    {
                        "id": "1",
                        "type": "locality"
                    },
                    {
                        "id": "2",
                        "type": "locality"
                    },
                    {
                        "id": "3",
                        "type": "locality"
                    },
                    {
                        "id": "4",
                        "type": "locality"
                    },
                    {
                        "id": "5",
                        "type": "locality"
                    },
                    {
                        "id": "6",
                        "type": "locality"
                    }
                ]
            },
            "attractions": {
                "data": [
                    {
                        "id": "1",
                        "type": "attraction"
                    }
                ]
            }
        }
    }
]

}

Is there a way to only include just one attribute (i.e. name of Country) in the Attraction JSON, instead of the whole object? (Attraction is nested two levels below Country)

Thank you very much.


Solution

  • You need to create multiple serializers that work together to achieve this.

    class AttractionSerializer
          include FastJsonapi::ObjectSerializer
    
          attributes :attraction_attr_1, :attraction_attr_2, etc.
    
          belongs_to :locality, serializer: AttractionLocalitySerializer
    end
    
    class AttractionLocalitySerializer
          include FastJsonapi::ObjectSerializer
    
          attributes :name
    
          belongs_to :country, serializer: AttractionCountrySerializer
    end
    
    class AttractionCountrySerializer
          include FastJsonapi::ObjectSerializer
    
          attributes :name
    end