Golar v0.5.1

API resources

github.com/dayanchm/golar/http/resource turns models into JSON responses. A resource is a function that names the fields to send, so a password hash or a newly added column never reaches clients unless a resource includes it.

func UserResource(ctx context.Context, u models.User) resource.Object {
    return resource.Object{
        "id":         u.ID,
        "name":       u.Name,
        "email":      resource.When(isAdmin(ctx), u.Email),
        "created_at": u.CreatedAt,
    }
}

app.Get("/api/users/{id}", func(c *golar.Context) error {
    id, err := strconv.ParseInt(c.Param("id"), 10, 64)
    if err != nil {
        return &golar.HTTPError{Code: http.StatusNotFound}
    }
    user, err := orm.Find[models.User](c.Context(), db, id)
    if err != nil {
        return err
    }
    return c.JSON(http.StatusOK, resource.Item(c.Context(), *user, UserResource))
})
{"data": {"id": 1, "name": "Ada", "created_at": "2026-10-29T10:00:00Z"}}

The context is the request's, so resources can read the authenticated user with auth.User(ctx).

Collections and pagination

resource.Collection(ctx, users, UserResource) // {"data": [...]}; nil encodes as []

users, page, err := orm.Query[models.User](db).OrderBy("id", "asc").Paginate(ctx, pageNumber, 15)
return c.JSON(http.StatusOK, resource.Paginated(ctx, users, page, UserResource, resource.PageURL(c.Request, "page")))
{
  "data": [{"id": 16, "name": "P"}, {"id": 17, "name": "Q"}],
  "meta": {"current_page": 2, "per_page": 15, "total": 32, "last_page": 3, "from": 16, "to": 17},
  "links": {"first": "/api/users?page=1", "last": "/api/users?page=3",
            "prev": "/api/users?page=1", "next": "/api/users?page=3"}
}

from and to are the 1-based positions of the page's first and last items, or null for an empty page. prev and next are null at the ends. A page past the end has prev pointing at the last page.

PageURL keeps the request's path and query and replaces the page parameter. Its links are relative, so a forged Host header cannot redirect clients. Pass nil instead to leave links out, or your own func(page int) string.

Conditional fields

Helper Result
resource.When(ok, value) value when ok, otherwise the field is left out
resource.WhenFunc(ok, func() any { ... }) builds the value only when ok
resource.WhenNotNil(ptr) *ptr, or left out for nil, such as a relation that was not loaded
obj.MergeWhen(ok, resource.Object{...}) adds several fields when ok
resource.Missing a value that leaves its field out

Missing fields are dropped from resource.Object values at every level, so nest resource.Object rather than map[string]any. A Missing value inside a slice is not removed.

Documents

Item, Collection and Paginated return a resource.Document. Set Extra for more top-level fields:

doc := resource.Collection(ctx, posts, PostResource)
doc.Extra = resource.Object{"version": "2026-10"}

data, meta and links cannot be replaced through Extra.

Edit this page on GitHub