I am trying to understand marshalling of JSON in Go. I am a little bit confused because of Go’s string nil value being ""
, which has a different semantic than a NULL
.
While my code currently works fine, I’m not happy with how I’m dealing with it and would love some advice to write it cleaner.
I have a db table called authors
with name
and bio
column.
I want to build a REST API that can update it’s value.
I want to only update the value if key is present in the json request.
{"name": "new name"} // only update "name"
{"name": "new name", "bio": ""} // update "name" and "bio"
This is my current code:
func (s *Server) UpdateAuthor(c *gin.Context) {
authorId := c.Param("id")
id, err := strconv.ParseInt(authorId, 10, 64)
if err != nil {
c.JSON(http.StatusUnprocessableEntity, gin.H{
"error": err.Error(),
})
return
}
author, err := s.Query.GetAuthor(c, id)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
var input UpdateAuthorInput
if err := c.BindJSON(&input); err != nil {
c.JSON(http.StatusUnprocessableEntity, gin.H{
"error": err.Error(),
})
return
}
updatedAuthor := input.data(author)
author, err = s.Query.UpdateAuthor(c, updatedAuthor)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
c.JSON(http.StatusOK, author)
}
type UpdateAuthorInput struct {
Name *string `json:"name"`
Bio *string `json:"bio"`
}
func (a UpdateAuthorInput) data(current data.Author) data.UpdateAuthorParams {
params := data.UpdateAuthorParams{
ID: current.ID,
Name: current.Name,
Bio: current.Bio,
}
if a.Name != nil {
params.Name = *a.Name
}
if a.Bio != nil {
params.Bio = sql.NullString{String: *a.Bio, Valid: true}
}
return params
}
sqlc generated type:
type UpdateAuthorParams struct {
Name string `json:"name"`
Bio sql.NullString `json:"bio"`
ID int64 `json:"id"`
}
func (q *Queries) UpdateAuthor(ctx context.Context, arg UpdateAuthorParams) (Author, error) {
row := q.db.QueryRowContext(ctx, updateAuthor, arg.Name, arg.Bio, arg.ID)
var i Author
err := row.Scan(&i.ID, &i.Name, &i.Bio)
return i, err
}
Everything works but I’m not happy with it because I have to check the property one by one.
I was wondering if there is a more cleaner and less error prone way to convert UpdateAuthorInput
-> data.UpdateAuthorParams
?