varnothing
varnothing

Reputation: 1309

restfull api on model with foreign-key

TLDR; How to create REST api on model having Foreign key (or db relationship in general) in buffalo framework?

I am absolute beginner in go and I am trying to write an RESTFul service using buffalo framework following following example given on it official website. I am able to create RESTful api on models which have no database relationship. But I am stuck when I faced a situation where model has a foreign key. I am not able to find any documentation or reference on web. I concept on Go is also weak, you can also educate me on those thins.

Models: (ref: https://gobuffalo.io/en/docs/db/relations#example

type Composer struct {
    ID              uuid.UUID `json:"id" db:"id"`
    Name            string    `json:"name" db:"name"`
    Description     string    `json:"description" db:"description"`
    CreatedAt       time.Time `json:"created_at" db:"created_at"`
    UpdatedAt       time.Time `json:"updated_at" db:"updated_at"`

}


type Track struct {
    ID          uuid.UUID           `json:"id" db:"id"`
    Title       string              `json:"title" db:"title"`
    Description string              `json:"description" db:"description"`
    Composer    Composer            `has_one:"composer" fk_id:"id"`
    CreatedAt   time.Time           `json:"created_at" db:"created_at"`
    UpdatedAt   time.Time           `json:"updated_at" db:"updated_at"`
}

Resources: (ref: https://gobuffalo.io/en/docs/resources)

type TrackResource struct {
    buffalo.Resource
}


func (v TrackResource) List(c buffalo.Context) error {
    tx, ok := c.Value("tx").(*pop.Connection)
    if !ok {
        return errors.WithStack(errors.New("no transaction found"))
    }

    pieces := &models.Tracks{}

    q := tx.PaginateFromParams(c.Params())

    if err := q.All(pieces); err != nil {
        return errors.WithStack(err)
    }

    c.Set("pagination", q.Paginator)

    return c.Render(200, r.JSON(pieces))
}


func (v TrackResource) Show(c buffalo.Context) error {
    tx, ok := c.Value("tx").(*pop.Connection)
    if !ok {
        return errors.WithStack(errors.New("no transaction found"))
    }

    piece := &models.Track{}

    if err := tx.Find(piece, c.Param("track_id")); err != nil {
        return c.Render(404, r.JSON(err))
    }

    return c.Render(200, r.JSON(piece))
}


func (v TrackResource) Create(c buffalo.Context) error {

    piece := &models.Track{}

    if err := c.Bind(piece); err != nil {
        return errors.WithStack(err)
    }

    tx, ok := c.Value("tx").(*pop.Connection)
    if !ok {
        return errors.WithStack(errors.New("no transaction found"))
    }

    verrs, err := piece.Create(tx)
    if err != nil {
        return errors.WithStack(err)
    }

    if verrs.HasAny() {
        return c.Render(422, r.JSON(verrs))
    }

    return c.Render(201, r.Auto(c, piece))
}


func (v TrackResource) Update(c buffalo.Context) error {

    tx, ok := c.Value("tx").(*pop.Connection)
    if !ok {
        return errors.WithStack(errors.New("no transaction found"))
    }

    piece := &models.Track{}

    if err := tx.Find(piece, c.Param("track_id")); err != nil {
        return c.Error(404, err)
    }

    if err := c.Bind(piece); err != nil {
        return errors.WithStack(err)
    }

    verrs, err := piece.Update(tx)
    if err != nil {
        return errors.WithStack(err)
    }

    if verrs.HasAny() {
        return c.Render(422, r.JSON(verrs))
    }

    return c.Render(200, r.JSON(piece))
}


func (v TrackResource) Destroy(c buffalo.Context) error {

    tx, ok := c.Value("tx").(*pop.Connection)
    if !ok {
        return errors.WithStack(errors.New("no transaction found"))
    }

    piece := &models.Track{}

    if err := tx.Find(piece, c.Param("track_id")); err != nil {
        return c.Error(404, err)
    }

    if err := tx.Destroy(piece); err != nil {
        return errors.WithStack(err)
    }

    return c.Render(200, r.JSON(piece))
}

When I am trying to create a track. I am getting error:

json: cannot unmarshal string into Go struct field Track.Composer of type models.Composer
gitlab.com/****/****/actions.TrackResource.Create

enter image description here

Please help.

Upvotes: 1

Views: 819

Answers (2)

Gavin
Gavin

Reputation: 4515

You should add a ComposerID field to your Track struct. Then you can use Eager or Load, and pop will load the associated Composer object for you. This is shown on the page you've linked to for your Models reference.

Upvotes: 0

Stanislas Michalak
Stanislas Michalak

Reputation: 361

You're trying to bind a Track containing a Composer with its ID, but Composer is defined as a struct.

To make it work, you need to implement the Unmarshaler interface and define how to convert this ID into the Composer struct you want.

Upvotes: 2

Related Questions