Every method on one resource, over the rows of the large item payload: a row read, its headers alone, and four writes that answer as if they had written.
Comparable across every framework. Each row sends one row-sized body or none and answers one row or nothing, so the methods differ in what the framework does with the request rather than in how much it serialises. A measured row may not leave the server changed, so the writes store nothing and no framework's store is what is measured.
{draw.item} is a row of items.large, picked per request.
| test | heft | request | base | what differs from the base |
|---|---|---|---|---|
| items.create | 2 | POST /items | body.bind_small | the body kept as a new resource and answered 201 with its location |
| items.replace | 2 | PUT /items/{draw.item} | items.create | the same resource through a different HTTP method |
| items.update | 2 | PATCH /items/{draw.item} | items.replace | the same resource through a different HTTP method |
| items.read | 1 | GET /items/{draw.item} | parameters.one | a row found by the id the route captured, instead of a fixed body |
| items.delete | 1 | DELETE /items/{draw.item} | items.read | the same resource through a different HTTP method |
| items.head | 1 | HEAD /items/{draw.item} | items.read | the same resource through a different HTTP method |
A new item posted as JSON and answered 201, with where it would live and what it would hold. Nothing is stored, so the answer is always the id after the last row. Read against body.bind_small, the difference is a body bound to a model and answered as a created resource rather than echoed with counts.
POST /items
payload items.new
{"name":"amber-trowel-4410","category":"garden","priceCents":2499,"inStock":true}HTTP 201
/^(https?:\/\/[^/]+)?\/items\/1426$/payload items.new as row 1426
{"id":1426,"name":"amber-trowel-4410","category":"garden","priceCents":2499,"inStock":true}Compared as parsed JSON, so key order and how a number is written do not matter.
import { performanceTest } from "#kit";
import { CREATED, created, items } from "#payloads";
const path = "/items";
/** Relative, or absolute on whatever host the framework thinks it is. */
const LOCATION = new RegExp(`^(https?://[^/]+)?/items/${CREATED}$`);
export default performanceTest({
id: { family: "items", name: "create" },
path,
base: "body.bind_small",
varies: "creation",
heft: 2,
about:
"A new item posted as JSON and answered 201, with where it would live and " +
"what it would hold. Nothing is stored, so the answer is always the id " +
"after the last row. Read against body.bind_small, the difference is a " +
"body bound to a model and answered as a created resource rather than " +
"echoed with counts.",
request: (c) => c.post(path, items.new.value).status(201).bodyIs(created).hasHeader("location", LOCATION),
});
A whole item put at an id and answered with the item under that id. Read against items.create, the difference is the id coming from the path rather than from the server.
PUT /items/{draw.item}payload items.new
{"name":"amber-trowel-4410","category":"garden","priceCents":2499,"inStock":true}HTTP 200
payload items.new as row {draw.item}
{"id":1417,"name":"amber-trowel-4410","category":"garden","priceCents":2499,"inStock":true}Compared as parsed JSON, so key order and how a number is written do not matter. Shown with 1417 for {draw.item}.
import { performanceTest } from "#kit";
import { items, replaced } from "#payloads";
const path = "/items/{draw.item}";
export default performanceTest({
id: { family: "items", name: "replace" },
path,
base: "items.create",
varies: "method",
heft: 2,
about:
"A whole item put at an id and answered with the item under that id. Read " +
"against items.create, the difference is the id coming from the path " +
"rather than from the server.",
request: (c) => {
const id = c.draw.item();
return c.put(`/items/${id}`, items.new.value).okWith(replaced(id));
},
});
Two fields patched onto a row and answered with the row as it would be. The handler has to read the row, merge the body into it and serialise the result. Read against items.replace, the difference is the merge.
PATCH /items/{draw.item}payload items.patch
{"priceCents":1999,"inStock":false}HTTP 200
payload row {draw.item} of items.large with items.patch applied
{"id":1417,"name":"slate-seed-6074","category":"garden","priceCents":1999,"inStock":false}Compared as parsed JSON, so key order and how a number is written do not matter. Shown with 1417 for {draw.item}.
import { performanceTest } from "#kit";
import { items, patched } from "#payloads";
const path = "/items/{draw.item}";
export default performanceTest({
id: { family: "items", name: "update" },
path,
base: "items.replace",
varies: "method",
heft: 2,
about:
"Two fields patched onto a row and answered with the row as it would be. " +
"The handler has to read the row, merge the body into it and serialise " +
"the result. Read against items.replace, the difference is the merge.",
request: (c) => {
const id = c.draw.item();
return c.patch(`/items/${id}`, items.patch.value).okWith(patched(id));
},
});
One row of the large payload, looked up by the id in the path. The id is drawn per request, so each instance reads a different row. Read against parameters.one, the difference is a lookup and one row serialised in place of an echo beside the small payload.
GET /items/{draw.item}No body.
HTTP 200
payload row {draw.item} of items.large
{"id":1417,"name":"slate-seed-6074","category":"garden","priceCents":9466,"inStock":true}Compared as parsed JSON, so key order and how a number is written do not matter. Shown with 1417 for {draw.item}.
import { performanceTest } from "#kit";
import { row } from "#payloads";
const path = "/items/{draw.item}";
export default performanceTest({
id: { family: "items", name: "read" },
path,
base: "parameters.one",
varies: "lookup",
heft: 1,
about:
"One row of the large payload, looked up by the id in the path. The id is " +
"drawn per request, so each instance reads a different row. Read against " +
"parameters.one, the difference is a lookup and one row serialised in " +
"place of an echo beside the small payload.",
request: (c) => {
const id = c.draw.item();
return c.get(`/items/${id}`).okWith(row(id));
},
});
A row deleted and answered 204 with no body. Nothing is removed, so every instance finds the row it names. Read against items.read, the difference is that nothing is serialised at all.
DELETE /items/{draw.item}No body.
HTTP 204
No body.
import { performanceTest } from "#kit";
const path = "/items/{draw.item}";
export default performanceTest({
id: { family: "items", name: "delete" },
path,
base: "items.read",
varies: "method",
heft: 1,
about:
"A row deleted and answered 204 with no body. Nothing is removed, so " +
"every instance finds the row it names. Read against items.read, the " +
"difference is that nothing is serialised at all.",
request: (c) => c.delete(`/items/${c.draw.item()}`).status(204).emptyBody(),
});
The same lookup asked with HEAD, which the framework answers from its GET route with no body. Read against items.read, the difference is the body left unwritten. Content-Length is not checked, because a framework that streams its JSON sends none on the GET either.
HEAD /items/{draw.item}No body.
HTTP 200
/^application\/json/No body.
import { performanceTest } from "#kit";
const path = "/items/{draw.item}";
export default performanceTest({
id: { family: "items", name: "head" },
path,
base: "items.read",
varies: "method",
heft: 1,
about:
"The same lookup asked with HEAD, which the framework answers from its GET " +
"route with no body. Read against items.read, the difference is the body " +
"left unwritten. Content-Length is not checked, because a framework that " +
"streams its JSON sends none on the GET either.",
request: (c) => c.head(`/items/${c.draw.item()}`).ok().hasHeader("content-type", /^application\/json/).emptyBody(),
});