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.
87 lines
3.9 KiB
Markdown
87 lines
3.9 KiB
Markdown
# 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](../surf/README.md) 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
|
|
|
|
```go
|
|
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
|
|
|
|
```sh
|
|
go test ./modules/wire/...
|
|
```
|
|
|
|
The tests use `net/http/httptest` and need no external services.
|