Fetch with Zod.
npm install kneel
import kneel from 'kneel'
import { z } from 'zod'
const output = await kneel({
url: 'http://localhost:3000',
o: z.object({ names: z.array(z.string()) })
})
const uppercaseNames = output.names.map((name) => name.toUpperCase())
const writeOutput = await kneel({
url: 'http://localhost:3000',
i: z.object({ name: z.string() }),
body: { name: 'Zelda Fitzgerald' },
o: z.object({ count: z.number() })
})
console.log(typeof writeOutput.count) // 'number'
Making validation functional can require patterns like annotating with unknown
.
import { z } from 'zod'
const outputSchema = z.object({ output: z.number() })
type Output = z.infer<typeof schema>
async function read (): Promise<Output> {
const response = await fetch('http://localhost:3000')
// Don't use the unvalidated data
const json: unknown = await response.json()
return outputSchema.parse(json)
}
const inputSchema = z.object({ input: z.string() })
type Input = z.infer<typeof schema>
async function write (input: Input): Promise<Output> {
const body = inputSchema.parse(input)
const response = await fetch('http://localhost:3000', {
method: 'POST',
body: JSON.stringify(body)
})
const json: unknown = await response.json()
return outputSchema.parse(json)
}
kneel
requires a Zod schema for a usable response. It also requires a schema if you include a body.
import kneel from 'kneel'
import { z } from 'zod'
const outputSchema = z.object({ output: z.number() })
type Output = z.infer<typeof outputSchema>
async function read (input: Input): Promise<Output> {
return kneel({
url: 'http://localhost:3000',
o: outputSchema
})
}
const inputSchema = z.object({ input: z.string() })
type Input = z.infer<typeof inputSchema>
async function write (input: Input): Promise<Output> {
return kneel({
url: 'http://localhost:3000',
i: inputSchema,
body: { input: 'increment' },
o: outputSchema
})
}
Parameter | Type | Description | Required | Default | Example |
---|---|---|---|---|---|
url |
string |
URL to fetch | Yes |
'http://localhost:3000' |
|
i |
ZodSchema |
Request body schema | No |
z.object({
input: z.string()
}) |
|
o |
ZodSchema |
Response body schema | No |
z.object({
output: z.number()
}) |
|
body |
z.infer<typeof i> |
Request body |
If |
{ input: 'hello' } |
|
method |
'GET'
| 'POST'
| 'PUT'
| 'DELETE'
| 'PATCH' |
The HTTP method | No |
|
'PUT' |
headers |
HeadersInit |
Request headers | No |
{ 'x-api-key': 'token' } |
|
encoding |
'application/x-www-form-urlencoded'
| 'multipart/form-data'
| 'text/plain'
| 'application/json' |
Request encoding | No |
'application/json' if |
'text/plain' |
debug |
boolean |
Print request, response, and errors | No |
false |
true |
kneel
takes a single object with one required parameter:
-
url
, a string
You can optionally set:
-
method
, a string -
headers
, matching the nativefetch
headers
You can optionaly set an output schema with:
-
o
, azod
schema
If there is an o
schema, kneel will parse the response body with .json()
and o.parse()
, then return it. If there is no o
schema, kneel
will return void
.
You can optionally include a request payload with:
-
i
, azod
schema -
body
, a value matching thei
schema
The request body
will be parse
d by the i
schema. By default including a body sets the method to 'POST'
.
By default the request body will be encoded with JSON.stringify()
and the Content-Type
header will be set to application/json
. You can override this with:
-
encoding
, which must be either'application/x-www-form-urlencoded'
,'multipart/form-data'
,'text/plain'
, or'application/json'
.
const inputSchema = z.object({ input: z.string() })
const response = await kneel({
url: 'http://localhost:3000',
i: inputSchema,
body: { input: 'hello' },
encoding: 'application/x-www-form-urlencoded',
})
The encoding
becomes the value of the Content-Type
header. application/x-www-form-urlencoded
uses new URLSearchParams()
to encode the body. multipart/form-data
uses new FormData()
. text/plain
uses String()
.
You can import the KneelProps
type to match the parameters of kneel
.
import kneel, { KneelProps } from 'kneel'
import { z, ZodSchema } from 'zod'
const fetchAndLog = async <
Input,
InputSchema extends ZodSchema<Input>,
Output = void
>(props: KneelProps<Input, InputSchema, Output>) => {
const response = await kneel(props)
console.log('Response:', response)
}
await fetchAndLog({
url: 'http://localhost:3000/hello',
o: z.literal('world')
})
// 'Response: world'
KneelProps
takes three generic parameters:
Parameter | Extends | Description | Default | Example |
---|---|---|---|---|
Input |
Request body |
{ input: string } |
||
InputSchema |
ZodSchema<Input> |
Request body schema |
ZodObject<{ name: ZodString }> |
|
Output |
Response body |
void |
{ output: number } |
You can create a kneel
function with custom parameter middleware using kneelMaker
.
import { kneelMaker } from 'kneel'
import { z } from 'zod'
const kneelHere = kneelMaker({
make: ({ url, ...rest }) => {
return { url: `http://localhost:3000${url}`, ...rest }
}
})
const outputSchema = z.literal('world')
const response = await kneelHere({
url: '/hello', // Request is sent to 'http://localhost:3000/hello'
o: outputSchema
})
console.log(response) // 'world'
Parameter | Type | Description | Required | Default | Example |
---|---|---|---|---|---|
make |
<
Input,
InputSchema extends ZodSchema<Input>,
Output = void
> (
props: KneelProps<Input, InputSchema, Output>
) => KneelProps<Input, InputSchema, Output> |
Custom props callback | Yes |
props => {
const { url, ...rest } = props
const urlHere = `http://localhost:3000${url}`
return { url: urlHere, ...rest }
} |
|
debug |
boolean |
Print input and output props | No |
false |
true |
kneelMaker
returns a custom function with the same parameters as kneel
.
Each time the custom function is called, the props will be passed to the make
callback, and the props make
returns will be passed to kneel
.