Skip to main content

Overview

The hypermedia middleware provides helpers for building HATEOAS (Hypermedia as the Engine of Application State) APIs with links and embedded resources. Use it when you need:
  • RESTful hypermedia APIs
  • Self-documenting responses
  • Discoverable APIs

Installation

import "github.com/go-mizu/mizu/middlewares/hypermedia"

Quick Start

app := mizu.New()

app.Use(hypermedia.New())

app.Get("/users/:id", func(c *mizu.Ctx) error {
    user := getUser(c.Param("id"))

    return hypermedia.JSON(c, 200, user,
        hypermedia.Self("/users/"+user.ID),
        hypermedia.Link("orders", "/users/"+user.ID+"/orders"),
    )
})

Configuration

Options

OptionTypeDefaultDescription
BaseURLstring""Base URL for links
Formatstring"hal"HAL or JSON:API

Examples

HAL Format

app.Get("/users/:id", func(c *mizu.Ctx) error {
    return hypermedia.HAL(c, 200, user,
        hypermedia.Self("/users/1"),
        hypermedia.Link("orders", "/users/1/orders"),
    )
})
// {
//   "id": 1,
//   "name": "John",
//   "_links": {
//     "self": {"href": "/users/1"},
//     "orders": {"href": "/users/1/orders"}
//   }
// }

Embedded Resources

app.Get("/users/:id", func(c *mizu.Ctx) error {
    return hypermedia.HAL(c, 200, user,
        hypermedia.Self("/users/1"),
        hypermedia.Embed("orders", orders),
    )
})

Collection with Pagination

app.Get("/users", func(c *mizu.Ctx) error {
    return hypermedia.Collection(c, 200, users,
        hypermedia.Self("/users"),
        hypermedia.Link("next", "/users?page=2"),
        hypermedia.Link("prev", "/users?page=0"),
    )
})

API Reference

Functions

// New creates hypermedia middleware
func New() mizu.Middleware

// HAL returns HAL+JSON response
func HAL(c *mizu.Ctx, status int, data any, links ...Link) error

// Collection returns HAL collection
func Collection(c *mizu.Ctx, status int, items any, links ...Link) error

// Link helpers
func Self(href string) Link
func Link(rel, href string) Link
func Embed(rel string, data any) Link

Technical Details

Architecture

The hypermedia middleware uses a response recorder pattern to intercept and modify JSON responses:
  1. Context Storage: Links are stored in the request context using a private context key
  2. Response Recording: A custom responseRecorder captures the response body and status code
  3. JSON Modification: The middleware parses the JSON response, injects links, and re-encodes it
  4. Content-Type Filtering: Only processes responses with application/json content type

Implementation Details

Link Structure:
  • Href: The URL of the linked resource
  • Rel: The relationship type (e.g., “self”, “next”, “prev”)
  • Method: Optional HTTP method for the link
  • Title: Optional human-readable description
  • Type: Optional media type hint
Response Recorder: The middleware implements a custom responseRecorder that wraps the original http.ResponseWriter to:
  • Capture response body in a buffer
  • Record status code
  • Allow modification before final write
HAL+JSON Support: The HAL type provides full HAL+JSON specification support with:
  • Properties for resource data
  • Links map for hypermedia links
  • Embedded map for nested resources
  • Custom JSON marshaling to flatten properties into the root object

Performance Considerations

  • Links are stored as pointers in context to avoid copying
  • JSON parsing only occurs for application/json responses
  • Non-JSON responses pass through without modification
  • Response buffering adds minimal overhead

Security Considerations

  • Base URL validation prevents injection attacks
  • Links are added server-side, not from user input
  • TLS detection for automatic scheme selection
  • No sensitive data should be exposed in link URLs

Best Practices

  • Always include self link
  • Use consistent link relations
  • Document link relations
  • Consider client library support

Testing

The hypermedia middleware includes comprehensive test coverage for all functionality:
Test CaseDescriptionExpected Behavior
TestNewBasic middleware initializationCreates middleware with default options and adds self link to JSON response
TestSelfLinkSelf link generation with base URLAutomatically adds self link with correct base URL and rel=“self”
TestAddLinkAdding single link to responseSuccessfully adds custom link to response links array
TestAddLinksAdding multiple links at onceAdds multiple links in a single call to the response
TestLinkProviderDynamic link generation via providerInvokes link provider function to generate context-aware links
TestNonJSONResponseNon-JSON response handlingPasses through non-JSON responses unchanged without adding links
TestCustomLinksKeyCustom links key configurationUses custom key instead of default “_links” for link storage
TestResourceResource wrapper with linksCreates resource structure with data and links fields
TestCollectionPaginated collection with linksGenerates collection with pagination metadata and navigation links (first, prev, next, last)
TestCollectionFirstPageFirst page paginationOmits “prev” link on first page of results
TestCollectionLastPageLast page paginationOmits “next” link on last page of results
TestHALHAL+JSON resource creationCreates HAL resource with properties and links, marshals correctly
TestHALEmbeddedHAL embedded resourcesEmbeds related resources within HAL response structure
TestGetLinksRetrieving current linksReturns current links from context during request handling