Planning to refactor the codebase of an app we built last year. Let's say we have this api endpoint w/c supports including a related resource GET /api/leagues?include=sport
. Is there a cleaner way to make the sport
attribute of the league
resource dynamic in the api response? The value of the sport attribute can be an int or Sport struct. Currently, this is what we're doing:
// models/league.go
type League struct {
SportId int64 `json:"-"`
Sport Sport `json:"-"`
SportWrapper interface{} `json:"sport"`
}
// controllers/league.go
include := c.Request.URL.Query().Get("include")
includes := strings.Split(include, ",")
for _, include := range includes {
if include == "sport" {
includeSport = true
}
}
if includeSport {
league.SportWrapper = league.Sport
} else {
league.SportWrapper = league.SportId
}
c.JSON(http.StatusOK, league)
response to the api request:
{
sport: {
id: <id>,
name: <name>
}
}
it can also be:
{
sport: <sport_id>
}
It's difficult to say without more context. I assume that the SportId is also part of the Sport struct. You are basically only returning the ID or the whole struct.
I would propose to only add the
Sport Sport `json:"-"`
struct. Instead of directly serializing it to json you would have to create a map (basically a viewmodel) first with the fixed attributes set. There you can set your SportID or Sport struct and after that you can serialize the map to json.
This is also not a very nice solution but the advantage is that in your current solution, the ugliness of your response code leaks into the data model and therefore into the rest of your application. With the intermediate map the ugly part is confined into the response generation.
// models/league.go
type League struct {
Sport Sport
}
... parse includes ...
viewModel := map[string]interface{}
if includeSport {
viewModel["sport"] = league.Sport
} else {
viewModel["sport"] = league.Sport.ID
}
c.JSON(http.StatusOK, viewModel)
// models/league.go
type League struct {
SportID int64 `json:"-"`
Sport Sport `json:"-"`
SportWrapper interface{} `json:"sport"`
}
var (
sportID int
sport Sport
)
// controllers/league.go
include := c.Request.URL.Query().Get("include")
if err := json.Unmarshal([]byte(unclude), &sportID); err != nil {
json.Unmarshal([]byte(unclude), &sport)
league.SportWrapper = sport
league.Sport = sport
} else {
league.SportWrapper = sportID
league.SportID = sportID
}
c.JSON(http.StatusOK, league)
if you could set your request param to json you can use this.