A generic, abstract mock-entity web API written in Go — define your own entity
types and data, and it exposes them as a fully-featured REST resource. Drop a
JSON file in the data/ directory and it instantly gets listing, pagination,
lookup, and filtering — no code changes required. Three sample datasets ship
with it — Marvel characters (characters), tech products (tech), and
supermarket products (groceries) — but the API itself has no idea what any
of them are, so you can swap in any dataset you like.
Every record served by the API is normalized into a generic envelope:
{
"id": 1,
"name": "Iron Man",
"slug": "iron-man",
"attributes": {
"realName": "Tony Stark",
"affiliation": "Avengers",
"actor": "Robert Downey Jr."
}
}idandnamecome straight from your source JSON (both required).slugis used for lookups; it's taken from your source JSON if present, otherwise auto-generated fromname(e.g."Scarlet Witch"->scarlet-witch).- Every other field you supply lands in
attributesuntouched, so each entity type can have a completely different shape.
-
Create
data/<yourtype>.jsoncontaining a JSON array of flat objects:[ { "id": 1, "name": "Sword", "damage": 12, "rarity": "common" }, { "id": 2, "name": "Shield", "defense": 8, "rarity": "rare" } ] -
Start (or restart) the server. The filename (without
.json) becomes both the type name and the route prefix, e.g.data/weapons.jsonis served at/api/v1/weapons.
Every record must include id (number) and name (string); the server
fails fast at startup with a descriptive error (file + record index) if either
is missing.
go run ./cmd/serverConfiguration is via environment variables:
| Variable | Default | Description |
|---|---|---|
PORT |
8080 |
TCP port the HTTP server listens on |
DATA_DIR |
./data |
Directory scanned for *.json type files |
Open http://localhost:8080/ in a browser and you get an interactive console
instead of raw JSON. It discovers your entity types from the live API, so the
type selector lets you switch between whatever data you dropped in DATA_DIR
(the bundled characters, tech, and groceries, or your own) — no
configuration.
Pick a type, set limit/offset, add filters (the key box suggests the exact
attribute spellings found on your records), and hit Send request. The
response panel shows the status, timing, body size and pretty-printed JSON;
Prev/Next follow the pagination links, and clicking a result row fetches
that single entity. There is also Copy as curl for taking a request you
built to the terminal.
The console is compiled into the binary, loads nothing from the network, and adds no dependencies — the whole page is one embedded HTML file.
All routes are prefixed with /api/v1.
Content-negotiated. Clients whose Accept header explicitly prefers
text/html — i.e. browsers — get the web console. Everybody
else, including curl and any client sending Accept: */*, gets the JSON
discovery document: the API name plus a link for every currently loaded entity
type, so clients can explore the API without prior knowledge of its data.
curl localhost:8080/ # JSON discovery document
curl -H 'Accept: text/html' localhost:8080/ # HTML console?format=json forces JSON, which is how you read the discovery document from
a browser.
The web console, served unconditionally regardless of Accept. Useful as a
stable link to share.
List every discovered entity type and its record count.
curl localhost:8080/api/v1/typesList entities of a type, paginated and optionally filtered.
Query parameters:
limit(default20, max100)offset(default0)- any other query parameter is treated as an equality filter against a core
field (
name,slug) or anattributesfield, e.g.?affiliation=Avengers
Filter semantics worth knowing:
- Attribute keys are case-sensitive (
realName, notrealname); onlynameandslugare matched case-insensitively. - Values are compared case-insensitively but must match exactly — there is no partial match, no full-text search, and no sorting.
- An unrecognised key returns zero results, not an error.
- Array-valued attributes (e.g.
powers) are stringified as[flight telekinesis], so they are effectively unfilterable.
curl "localhost:8080/api/v1/characters?limit=2&offset=0"
curl "localhost:8080/api/v1/characters?affiliation=Avengers"Response shape:
{
"count": 64,
"next": "http://localhost:8080/api/v1/characters?limit=2&offset=2",
"previous": null,
"results": [ { "id": 1, "name": "Iron Man", "slug": "iron-man", "attributes": { } } ]
}next and previous are absolute URLs. They honour X-Forwarded-Proto, so
they come back as https:// behind a TLS-terminating proxy such as Render's.
Fetch a single entity by numeric ID or by slug.
curl localhost:8080/api/v1/characters/1
curl localhost:8080/api/v1/characters/iron-manThe API is read-only: entity data is defined by the JSON files in DATA_DIR
and loaded at startup. To change it, edit those files and restart the server.
docker build -t gomock .
docker run -p 8080:8080 gomockTo use your own custom data instead of the bundled samples, mount a volume
over /app/data:
docker run -p 8080:8080 -v "$(pwd)/mydata:/app/data" gomockA render.yaml Blueprint is included so the service definition lives in source control:
- Push this repo to GitHub (already done if you're reading this from there).
- In the Render dashboard, choose
New > Blueprint and connect this repository. Render will detect
render.yamland provision a freedockerweb service automatically. - Render injects its own
PORTenvironment variable at runtime (overriding the Dockerfile default), so no extra configuration is needed.
Alternatively, without the Blueprint: New > Web Service > connect the
repo > Environment: Docker > Instance Type: Free.
Note: Render's free tier spins the service down after a period of inactivity, so the first request after idling will be slow (cold start).
- The API is read-only over HTTP; datasets are defined by the JSON files in
DATA_DIRand loaded at startup, so there is no runtime write surface to secure. This project is intended for local/dev mock use. - The project has no third-party dependencies;
go.modlists none and there is nogo.sum. The console is plain embedded HTML for the same reason.