Request samples
The same request in twelve clients, and how to add your own.
Beside the client, the request the reader has built — written out in the language they work in, and rewritten on every keystroke.
What ships
| Group | Clients |
|---|---|
curl | curl |
| TypeScript | fetch, $fetch, useFetch, axios |
| Python | requests, httpx, urllib |
| Go | net/http |
| PHP | Guzzle, Laravel Http, curl extension |
Seven are on by default: curl, fetch, $fetch, Python requests, Go, Guzzle
and Laravel Http. The other five are the second client for a language that
already has one on screen, and a picker is for choosing rather than for listing
everything that exists.
Two levels
The language is the tab and the client a select inside it, so PHP with three clients costs one tab. A site adding three of its own for one language still costs one. The reader's choice is remembered across pages — a reader who works in curl works in curl on the next endpoint too.
Configuring the list
export default defineAppConfig({
duxt: {
requestSamples: ['curl', 'php-guzzle', 'php-laravel']
}
})
A string picks one that ships; the ids are curl, fetch, ofetch,
use-fetch, axios, python-requests, python-httpx, python-urllib, go,
php-guzzle, php-laravel, php-curl.
The list replaces rather than extends, like every other list in the config: otherwise a site documenting a PHP API could add its clients but never drop the ones it does not want. The order is the order they appear in.
Your own generator
An object adds one of your own — or replaces one that ships, by reusing its id.
requestSamples: [
'curl',
{
id: 'ruby',
group: 'Ruby',
label: 'Net::HTTP',
language: 'ruby',
generate: (request) => `
uri = URI('${request.url}')
Net::HTTP.get(uri)
`.trim()
}
]
| Field | Type | Notes |
|---|---|---|
id | string | Stable: it is also the remembered value |
group | DuxtText | The tab |
label | DuxtText | The entry inside the tab |
language | string | A Shiki id — it decides the grammar |
generate | (request) => string | Synchronous, pure, no network, no DOM |
generate runs in the browser, on every keystroke, because the sample has to
follow the request being edited. request is { method, url, headers, body?, responseType? },
where body is the editor's text and not parsed — a generator that wants it as
data parses it itself, and gets to decide what to do when it is not JSON.
Why TypeScript and not JavaScript
$fetch, useFetch and axios all take the response shape as a type parameter,
and responseType is what fills it: $fetch<Widget>(…) is the line somebody
actually writes. fetch has none — it returns a Response whatever the body
turns out to be — so its sample is the same in either language.
That is also why there is one group and not two. A JavaScript group beside this
one would carry a fetch that never differs and three clients that differ only
where the document named its response schema, for twice the tabs.
responseType is the component name of what a successful response returns —
Widget, or Widget[] for a list. It is absent where the response schema is
inline rather than a $ref, and a sample then simply has no type parameter:
nothing here invents a name the document did not give.
What a sample shows
The request, and nothing after it. The response is already on screen in the panel below the editor, so a sample that fetched it a second time would be showing the reader something they can see — at the cost of an error branch in every language that has one.
Go is the exception and keeps its err checks: without them it is not Go, and a
snippet that does not compile is worse than a long one.
Grammars are paid for, not assumed
These samples are coloured in the browser, so every language is bytes a reader downloads — unlike a Markdown fence, whose grammar is a build-time cost and never reaches a browser at all.
So the grammar set is generated from the list above: a site ships exactly the languages its samples name. The default seven need six grammars. Loading Shiki's own lazy map instead would cost 3 KB and make every build emit 242 grammar chunks.
Samples from the document
x-codeSamples — under either spelling, the older x-code-samples included — is
read off an operation, and replaces the generated samples for its language.
The person who owns the API knows its idioms better than a generator does.
paths:
/pets:
get:
x-codeSamples:
- lang: ruby
label: Net::HTTP
source: |
Net::HTTP.get(URI('https://api.example.com/pets'))
Such a sample is static — it cannot follow the reader's edits — so the panel says who wrote it and that it does not follow the edits above. A reader who changed the body and switched to it would otherwise conclude the editor is broken rather than that this sample is fixed.
Its language is not visible where the grammar set is written: a remote source has not been cloned yet. So name it:
duxt: {
sampleLanguages: ['ruby']
}
Without that the sample renders as plain text.