What a page cannot say about itself — its canonical address, its indexing directive, the collection it belongs to, and the pages it references.
It does not return the page as HTML. A caller asking for text/markdown is answered with the page’s published Markdown, under a validator of its own, and a Link naming the address that Markdown publishes itself at.
This resource answers HEAD as well, with the same headers and no body.
It also answers OPTIONS, which returns Allow, a Link to this description, and — where the resource takes one — the query formats it reads, as Accept-Query.
The value filling {path}. This API's own template spells it {+path}, and OpenAPI has no way to say so — § 3.5 forbids an unescaped / in a path parameter, and there is no spelling for a value that may span several segments. So the value goes in with its separators intact and each segment percent-encoded on its own. Send writing/deep-modules, not writing%2Fdeep-modules — the two address different things, and only the first is this resource.
Named as a deviation rather than left to read as conformance.
---
title: "One page"
description: "What a page cannot say about itself — its canonical address, its indexing directive, the collection it belongs to, and the pages it references.\n\nIt does not return the page as HTML. A caller asking for `text/markdown` is answered with the page’s published Markdown, under a validator of its own, and a `Link` naming the address that Markdown publishes itself at.\n\nThis resource answers `HEAD` as well, with the same headers and no body.\n\nIt also answers `OPTIONS`, which returns `Allow`, a `Link` to this description, and — where the resource takes one — the query formats it reads, as `Accept-Query`."
url: "https://lateano.com/developers/api/reference/reading/page.get"
author: "Colin Lateano"
---
# One page
What a page cannot say about itself — its canonical address, its indexing directive, the collection it belongs to, and the pages it references.
It does not return the page as HTML. A caller asking for `text/markdown` is answered with the page’s published Markdown, under a validator of its own, and a `Link` naming the address that Markdown publishes itself at.
This resource answers `HEAD` as well, with the same headers and no body.
It also answers `OPTIONS`, which returns `Allow`, a `Link` to this description, and — where the resource takes one — the query formats it reads, as `Accept-Query`.
```json
{
"openapi": "3.1.1",
"info": {
"title": "lateano.com",
"version": "1.0.0"
},
"paths": {
"/api/pages/{path}": {
"get": {
"operationId": "page.get",
"summary": "One page",
"tags": [
"Reading"
],
"description": "What a page cannot say about itself — its canonical address, its indexing directive, the collection it belongs to, and the pages it references.\n\nIt does not return the page as HTML. A caller asking for `text/markdown` is answered with the page’s published Markdown, under a validator of its own, and a `Link` naming the address that Markdown publishes itself at.\n\nThis resource answers `HEAD` as well, with the same headers and no body.\n\nIt also answers `OPTIONS`, which returns `Allow`, a `Link` to this description, and — where the resource takes one — the query formats it reads, as `Accept-Query`.",
"parameters": [
{
"name": "path",
"in": "path",
"required": true,
"schema": {
"type": "string"
},
"description": "The value filling `{path}`. **This API's own template spells it `{+path}`, and OpenAPI has no way to say so** — § 3.5 forbids an unescaped `/` in a path parameter, and there is no spelling for a value that may span several segments. So the value goes in with its separators intact and each segment percent-encoded on its own. Send `writing/deep-modules`, not `writing%2Fdeep-modules` — the two address different things, and only the first is this resource.\n\nNamed as a deviation rather than left to read as conformance."
}
],
"responses": {
"200": {
"description": "One page",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PageBody"
}
}
}
},
"304": {
"description": "The representation this request already holds is still current, so it is not sent again. No body. Read the validator off `ETag` and send it back as `If-None-Match`.",
"headers": {
"etag": {
"schema": {
"type": "string"
}
},
"cache-control": {
"schema": {
"type": "string"
}
}
}
},
"default": {
"description": "Every other status this operation answers with is a refusal, and carries `problem+json` (RFC 9457).\n\nEvery refusal this API can make, whatever the status. Match on the `type` URI’s last segment — the slug below — rather than on the whole URI, which follows whichever deployment answered.\n\n| Status | Slug | Title |\n| --- | --- | --- |\n| 400 | [`invalid-idempotency-key`](https://lateano.com/api/problems/invalid-idempotency-key) | The Idempotency-Key header carries a value this resource will not accept |\n| 400 | [`malformed-body`](https://lateano.com/api/problems/malformed-body) | The request body did not parse |\n| 400 | [`missing-idempotency-key`](https://lateano.com/api/problems/missing-idempotency-key) | This resource requires an Idempotency-Key header |\n| 400 | [`missing-media-type`](https://lateano.com/api/problems/missing-media-type) | The request carried a body with no media type |\n| 404 | [`no-such-resource`](https://lateano.com/api/problems/no-such-resource) | There is nothing at this address |\n| 405 | [`method-not-allowed`](https://lateano.com/api/problems/method-not-allowed) | This resource does not answer that method |\n| 406 | [`not-acceptable`](https://lateano.com/api/problems/not-acceptable) | This resource cannot answer in any media type the request accepts |\n| 409 | [`conflicting-idempotency-key`](https://lateano.com/api/problems/conflicting-idempotency-key) | That Idempotency-Key already names a different submission |\n| 412 | [`precondition-failed`](https://lateano.com/api/problems/precondition-failed) | The condition the request carried does not hold |\n| 415 | [`unsupported-media-type`](https://lateano.com/api/problems/unsupported-media-type) | The request body is in a media type this resource does not read |\n| 422 | [`invalid-query`](https://lateano.com/api/problems/invalid-query) | The query parsed but does not describe a question this resource can answer |\n| 422 | [`invalid-submission`](https://lateano.com/api/problems/invalid-submission) | The submission parsed but is not something this resource can record |\n| 429 | [`too-many-requests`](https://lateano.com/api/problems/too-many-requests) | The courtesy quota for this caller is spent |\n| 500 | [`unexpected-failure`](https://lateano.com/api/problems/unexpected-failure) | The resource failed while answering |",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/ProblemDocument"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"PageBody": {
"type": "object",
"properties": {
"page": {
"$ref": "#/components/schemas/PageInfo"
}
},
"required": [
"page"
],
"additionalProperties": false,
"description": "An envelope with one key rather than a bare `PageInfo`. The resource can then gain a top-level field."
},
"ProblemDocument": {
"type": "object",
"properties": {
"type": {
"type": "string",
"description": "§ 3.1.1. Absolute, on the origin that answered, and it resolves."
},
"title": {
"type": "string",
"description": "§ 3.1.3. The catalogue row's, unchanged from occurrence to occurrence."
},
"status": {
"type": "number",
"description": "§ 3.1.2. The same number as the status line, derived rather than passed."
},
"instance": {
"type": "string",
"description": "§ 3.1.5 RECOMMENDS an absolute URI here. This field carries a full path from the root, which is the fallback form the same section permits."
},
"detail": {
"type": "string",
"description": "§ 3.1.4. This occurrence only, bounded by `DETAIL_MAX`, absent when there is nothing to add."
}
},
"required": [
"type",
"title",
"status",
"instance"
],
"additionalProperties": false,
"description": "The schema generator publishes this declaration as the error schema, so a field here reaches the reference. RFC 9457 § 3.1 makes all five members optional, and this API narrows four of them to required: § 3.1.1 reads an absent `type` as `about:blank` and discards the resolvable URI. An optional member is absent, never present and `undefined`, because `JSON.stringify` drops the value and the wire cannot tell the two apart."
},
"PageInfo": {
"type": "object",
"properties": {
"path": {
"type": "string"
},
"canonical": {
"type": "string"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"indexing": {
"type": "string",
"description": "The page's own `robots` directive, so a consumer can honour it."
},
"collection": {
"anyOf": [
{
"$ref": "#/components/schemas/Collection"
},
{
"type": "null"
}
],
"description": "`null` for a page no document backs — the homepage, an archive, About."
},
"date": {
"type": [
"string",
"null"
],
"description": "A post's publish date or a term's last review; `null` for a page no document backs, or for a Demo."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Tag titles as a reader types them. Empty rather than `null` for a page carrying none; empty and absent are one fact to every reader."
},
"aliases": {
"type": "array",
"items": {
"type": "string"
},
"description": "Other names for a term. Empty for a post and for a page nothing authored."
},
"twin": {
"type": [
"string",
"null"
],
"description": "The markdown twin's address, or `null` where the page has none."
},
"outgoingReferences": {
"type": "array",
"items": {
"$ref": "#/components/schemas/PageReference"
},
"description": "One resolved page edge."
},
"referencingPosts": {
"type": "array",
"items": {
"$ref": "#/components/schemas/PageReference"
}
},
"related": {
"type": "array",
"items": {
"$ref": "#/components/schemas/PageReference"
}
}
},
"required": [
"path",
"canonical",
"title",
"description",
"indexing",
"collection",
"date",
"tags",
"aliases",
"twin",
"outgoingReferences",
"referencingPosts",
"related"
],
"additionalProperties": false,
"description": "What any address on this site can say about itself."
},
"Collection": {
"type": "string",
"enum": [
"writing",
"glossary",
"demos"
]
},
"PageReference": {
"type": "object",
"properties": {
"path": {
"type": "string"
},
"title": {
"type": "string"
}
},
"required": [
"path",
"title"
],
"additionalProperties": false
}
}
}
}
```