GoBuffalo Nested Resources
[Buffalo](https://www.gobuffalo.io/) is a web framework written in Go by [Mark Bates](https://twitter.com/markbates). I've been working more with Go and started using Buffalo. Mark has done an awesome job with it! Thanks Mark! Pretty quickly into my app I wanted nested resources. Support for them seems pretty much there. It takes some work, but all in all it was pretty easy to get going. Here are some notes about how I approached it.
Buffalo is a web framework written in Go by Mark Bates. I've been working more with Go and started using Buffalo. Mark has done an awesome job with it! Thanks Mark!
Pretty quickly into my app I wanted nested resources. Support for them seems pretty much there. It takes some work, but all in all it was pretty easy to get going. Here are some notes about how I approached it.
Getting started
I started following some of the videos on the Buffalo site. From there I generated some resources; User, Org, Index. I wanted Indexes to be nested under Orgs.
Routing
actions/app.go
indices := app.Resource("/orgs/{org_id}/indices", IndicesResource{&buffalo.BaseResource{}})
indices.Middleware.Use(LoadOrg)
There we grab a reference to the resource group, named indices
and then add
the LoadOrg
middleware.
Middleware
Every action in the Indices resource should have the Org available. I was looking
for some BeforeAction
hook or something, but didn't find any. Enter middleware.
actions/orgs.go
func LoadOrg(next buffalo.Handler) buffalo.Handler {
return func(c buffalo.Context) error {
tx := c.Value("tx").(*pop.Connection)
user := c.Value("current_user").(*models.User)
o := &models.Org{}
if err := tx.BelongsToThrough(user, "org_users").Find(o, c.Param("org_id")); err != nil {
c.Error(404, err)
}
c.Set("org", o)
return next(c)
}
}
This does two things...
- Make sure the Org is available to the User.
- Set
org
in theContext
Actions
The actions need some updating, mostly updating them to scope the queries to the Org, make sure new Indexes get added to the Org, and changing some of the redirect URLs.
actions/indices.go
func (v IndicesResource) scope(c buffalo.Context) *pop.Query {
tx := c.Value("tx").(*pop.Connection)
org := c.Value("org").(*models.Org)
return tx.BelongsTo(org)
}
func (v IndicesResource) newIndex(c buffalo.Context) *models.Index {
orgID, err := uuid.FromString(c.Param("org_id"))
if err != nil {
c.Error(404, err)
}
return &models.Index{OrgID: orgID}
}
Those two helper functions help scope the queries and set the OrgID on new model instances.
I'm not showing all the changes needed, they'll be pretty obvious.
Views
I was kinda worried about this, but it turned out to already be supported. I'm not sure if it's part of Mux or Buffalo yet, but either way it works.
What works? You're wondering... links!
templates/indices/*.html
<!-- Generated Version -->
<li><a href="<%= newIndicesPath() %>" class="btn btn-primary">Create New Index</a></li>
<!-- Nested Version -->
<li><a href="<%= newOrgIndicesPath({org_id: org.ID}) %>" class="btn btn-primary">Create New Index</a></li>
Adding Org
or org
to the path function and passing in the org_id
value
was all that was needed. And the org
is in the Context
already thanks to
out middleware above!
Would love to know if I missed something already built in to the generators or a better way to handle any of this, but pretty happy with the results.
Webmentions
These are webmentions via the IndieWeb and webmention.io. Mention this post from your site: