Files
summercms/modules/wire
Jakub Zych 602e0412f3 docs: add a Role in the framework section to every module README
Each module README now says where the module sits in the dependency
graph, why it is its own module, and lists use cases for application
developers.
2026-10-11 00:58:17 +02:00
..

wire

JSON response helpers and value types that keep API bodies byte-compatible with a PHP (WinterCMS/Laravel) backend.

import "git.golem15.com/golem15/summercms/modules/wire"

Name: What goes over the wire; it keeps response bodies byte-for-byte the same as the PHP backend's.

Overview

wire is the lowest layer of the HTTP stack: it decides how a Go value becomes response bytes. Its helpers reproduce what json_encode and Carbon produce in a WinterCMS or Laravel app (unescaped HTML characters, no trailing newline, +00:00 timestamps, nullable booleans, [] rather than null for empty lists), so an endpoint ported from PHP returns the same body its existing clients already parse. surf uses it for the opaque 500 response of its panic recovery, and application handlers use it directly for their JSON responses.

Role in the framework

A dependency-free leaf for response shapes. surf and lighthouse use it, and so do plugin controllers. It is separate so every handler produces the same JSON as the PHP backend being replaced, byte for byte.

Use cases:

  • Return paginated lists and error envelopes in the shape an existing frontend expects.
  • Encode timestamps, nullable booleans and empty lists the way Laravel serializes them.
  • Port endpoints one at a time without the frontend noticing the backend changed.

Features

  • wire.WriteJSON encodes a value with HTML escaping disabled and without the trailing newline encoding/json adds, then sets Content-Type: application/json and the status code. If encoding fails, it writes the opaque 500 body instead of a partial response.
  • wire.WriteOpaque500 writes a 500 response with the fixed body {"error":true,"message":"Internal server error"}, which reveals nothing about the failure.
  • wire.Time wraps time.Time and always marshals in UTC as 2006-01-02T15:04:05+00:00 (Carbon's form, never Go's Z). It unmarshals a timestamp with a numeric offset or an RFC 3339 Z timestamp, and turns null into the zero time.
  • wire.TriBool models a nullable boolean: when wire.TriBool.Valid is false it marshals as null, otherwise as wire.TriBool.Value.
  • wire.Slice returns a non-nil empty slice for a nil input, so optional lists marshal as [] instead of null.

Usage

package blog

import (
	"net/http"
	"time"

	"git.golem15.com/golem15/summercms/modules/wire"
)

type postJSON struct {
	ID          uint         `json:"id"`
	Title       string       `json:"title"`
	Tags        []string     `json:"tags"`
	Featured    wire.TriBool `json:"featured"`
	PublishedAt wire.Time    `json:"published_at"`
}

func showPost(w http.ResponseWriter, r *http.Request) {
	var tags []string // nil when the post has no tags
	body := postJSON{
		ID:          1,
		Title:       "Hello & welcome",           // "&" stays unescaped
		Tags:        wire.Slice(tags),            // marshals as []
		Featured:    wire.TriBool{},              // marshals as null
		PublishedAt: wire.Time{Time: time.Now()}, // "...+00:00"
	}
	wire.WriteJSON(w, http.StatusOK, map[string]any{"data": body})
}

API reference

Identifier Description
wire.WriteJSON Writes a JSON body with HTML escaping off and no trailing newline; falls back to the opaque 500 on an encoding error.
wire.WriteOpaque500 Writes the fixed {"error":true,"message":"Internal server error"} 500 response.
wire.Time time.Time wrapper that marshals as UTC +00:00 and reads both +00:00 and Z forms.
wire.TriBool Nullable boolean: wire.TriBool.Valid false marshals null, otherwise wire.TriBool.Value.
wire.Slice Generic helper that turns a nil slice into an empty one so it marshals as [].

Dependencies

  • SummerCMS modules: none.
  • Third-party: none.
  • Standard library: bytes, encoding/json, net/http, time.

Testing

go test ./modules/wire/...

The tests use net/http/httptest and need no external services.