api

package
v6.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 8, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Server-only half of Atlas's typed API boundary.

The build constraint is load-bearing and `gwc server gen` refuses to run without it: this file's functions are the REAL implementations, and the generated client stub declares functions with the same names. If both entered the wasm build they would collide, so the constraint keeps the implementations out of the browser binary and the stub takes their place there.

It also means the database driver, the query layer and everything they pull in stay out of app.wasm — which is not a detail. The v5 plan's M5 size budget is the reason the two-artifact split exists at all; a server function that accidentally reached the client would drag its whole dependency graph with it.

Package api is Atlas's typed server-function boundary.

What this package is for

Everything Atlas's browser half needs from its server half crosses here, as an ordinary Go function call. `gwc server gen` reads the //gwc:server directives in server_functions.go and writes two files from ONE signature:

serverfn_gen_server.go  registers the real function on the HTTP mux
serverfn_gen_client.go  a stub with the same signature that calls it

So a component calls api.ListWarehouses(ctx, req) and gets a typed response, with no URL string, no fetch, no json.Unmarshal, and no place for the two sides to disagree. If the response type gains a field, both halves gain it together; if a caller passes the wrong type, it does not compile.

That is worth stating against what it replaces. Atlas used to build request URLs by string concatenation in legacy_shared.go — "/api/public/warehouses?" + encoded, "/api/app/products?" + encoded — decode the reply into an any-shaped payload, and hope the server still returned what the caller expected. Nothing checked the two ends against each other; a renamed JSON field failed at runtime, in the browser, as an empty region of the page.

Why the types live in this file and not next to the functions

server_functions.go carries `//go:build !js || !wasm` because it imports the database and must never enter the wasm binary. The generated client stub takes its place there. But BOTH halves need the request and response types, so the types sit here, unconstrained, and are compiled into both binaries.

The codegen enforces a related rule: a server function's request and response types must be declared in THIS package. That is why these are Atlas-owned structs rather than aliases of server/db types — the wire contract is a thing Atlas owns and versions deliberately, not an accident of whatever shape the storage layer happens to have today. A column rename in db should be a mapping change here, not a silent change to what the browser receives.

Index

Constants

This section is empty.

Variables

View Source
var ErrWarehouseSourceUnset = errors.New("api: warehouse source is not registered; call api.SetWarehouseSource during server startup")

ErrWarehouseSourceUnset is returned when ListWarehouses is called before the server wired its dependency.

A named error rather than a generic one because this failure has exactly one cause — a startup ordering mistake — and the message should say so instead of surfacing in the browser as an empty list, which is what a nil-check-and-return would have produced.

Functions

func RegisterServerFunctions

func RegisterServerFunctions(parseMux *http.ServeMux)

RegisterServerFunctions wires every //gwc:server function in this package onto mux. Call it once when building the server's HTTP handler.

func SetWarehouseSource

func SetWarehouseSource(parseSource WarehouseSource)

SetWarehouseSource installs the implementation behind ListWarehouses. The server calls this during startup, before it begins serving.

Guarded by a mutex rather than assigned directly because tests replace it while other tests may be reading it, and a data race here is the kind that passes for months and then fails once in CI.

Types

type ListWarehousesRequest

type ListWarehousesRequest struct {
	// Region optionally narrows the result. Empty means every hub.
	Region string `json:"region,omitempty"`
}

ListWarehousesRequest has no fields yet.

It exists anyway, rather than the function taking a bare struct{}, because a request type is where the first filter will go — and adding a field to an existing named type is a source-compatible change on both sides, whereas changing the parameter type is not.

type ListWarehousesResponse

type ListWarehousesResponse struct {
	Warehouses []Warehouse `json:"warehouses"`
}

ListWarehousesResponse wraps the slice instead of returning []Warehouse.

A bare slice is a dead end: the day this needs a total, a cursor, or a "generated at" stamp, every caller has to change. A struct absorbs that.

func ListWarehouses

func ListWarehouses(parseCtx context.Context, parseRequest ListWarehousesRequest) (ListWarehousesResponse, error)

ListWarehouses returns the fulfilment hubs a buyer is allowed to see.

type Warehouse

type Warehouse struct {
	ID            string `json:"id"`
	Slug          string `json:"slug"`
	Name          string `json:"name"`
	Region        string `json:"region"`
	ServiceLevel  string `json:"serviceLevel"`
	PublicSummary string `json:"publicSummary"`
}

Warehouse is the public projection of a fulfilment hub.

Deliberately NOT server/db.Warehouse: this is what the browser is allowed to know. Operator-only fields (staffing, backlog, pressure) exist on the storage type and must not arrive here just because someone added them upstream.

type WarehouseSource

type WarehouseSource func(parseCtx context.Context) ([]Warehouse, error)

WarehouseSource is the shape Atlas's storage layer must satisfy to serve ListWarehouses.

A server function is a plain top-level func — the codegen requires exactly (context.Context, Request) (Response, error) — so it cannot take a *db.Store parameter. Dependencies arrive through this seam instead, registered once at startup by the server's main.

Declaring the dependency as a function type rather than importing server/db keeps the direction of the dependency right: api defines what it needs, and the server supplies it. api never imports db, so db can never import api back into a cycle, and a test can register a fake in one line.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL