kneel
v0.1.25
Published
Fetch with zod
Maintainers
Readme
kneel
Fetch with Zod.
Installation
npm install kneelUsage
import kneel from 'kneel'
import { z } from 'zod'
const response = await kneel({
url: 'http://localhost:3000',
output: z.object({ names: z.array(z.string()) })
})
const uppercaseNames = response.names.map((name) => name.toUpperCase())
const writeResponse = await kneel({
url: 'http://localhost:3000',
inputSchema: z.object({ name: z.string() }),
input: { name: 'Zelda Fitzgerald' },
outputSchema: z.object({ count: z.number() })
})Problem
Making validation functional can require patterns like annotating with unknown.
Without kneel
import { z } from 'zod'
const outputSchema = z.object({ count: 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({ name: 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)
}Solution
kneel requires a Zod schema for a usable response. It also requires a schema if you include a body.
With kneel
import kneel from 'kneel'
import { z } from 'zod'
const outputSchema = z.object({ count: z.number() })
type Output = z.infer<typeof outputSchema>
async function read (input: Input): Promise<Output> {
return kneel({
url: 'http://localhost:3000',
outputSchema
})
}
const inputSchema = z.object({ name: z.string() })
type Input = z.infer<typeof inputSchema>
async function write (input: Input): Promise<Output> {
return kneel({
url: 'http://localhost:3000',
input,
inputSchema,
outputSchema
})
}Parameters
urlstring'http://localhost:3000'inputSchemaZodSchemaz.object({
name: z.string()
})outputSchemaZodSchemaz.object({
count: z.number()
})inputz.infer<typeof input>If inputSchema is set
{ name: 'Ada' }method'GET'
| 'POST'
| 'PUT'
| 'DELETE'
| 'PATCH''GET',
'POST' if inputSchema is set
'PUT'headersHeadersInit{ 'x-api-key': 'token' }contentType'application/x-www-form-urlencoded'
| 'multipart/form-data'
| 'text/plain'
| 'application/json''application/json'if inputSchema is set
'text/plain'debugbooleanfalsetruekneel takes a single object with one required parameter:
url, a string
You can optionally set:
method, a stringheaders, matching the nativefetchheaders
Output
You can optionaly set an output schema with:
outputSchema, azodschema
If there is an outputSchema, kneel will parse the response body with .json()and outputSchema.parse(), then return it. If there is no outputSchema, kneel will return void.
Input
You can optionally include a request payload with:
inputSchema, azodschemainput, a value matching theischema
The input will be parsed by the inputSchema. By default including input sets the method to 'POST'.
Content Type
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({ value: z.string() })
const response = await kneel({
url: 'http://localhost:3000',
input: { value: 'hello' },
inputSchema,
contentType: 'application/x-www-form-urlencoded',
})The contentType becomes the value of the Content-Type header. application/x-www-form-urlencoded encodes the body with new URLSearchParams(). multipart/form-data uses new FormData(). text/plain uses String().
KneelProps
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 <
RequestBody,
InputSchema extends ZodSchema<RequestBody>,
ResponseBody = void
>(props: KneelProps<RequestBody, InputSchema, ResponseBody>) => {
const response = await kneel(props)
console.log('Response:', response)
}
await fetchAndLog({
url: 'http://localhost:3000/hello',
outputSchema: z.literal('world')
})
// 'Response: world'KneelProps takes three generic parameters:
RequestBody{ name: string }InputSchemaZodSchema<RequestBody>ZodObject<{ name: ZodString }>ResponseBodyvoid{ count: number }kneelMaker
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'
outputSchema
})
console.log(response) // 'world' make<
RequestBody,
InputSchema extends ZodSchema<RequestBody>,
ResponseBody = void
> (
props: KneelProps<RequestBody, InputSchema, ResponseBody>
) => KneelProps<RequestBody, InputSchema, ResponseBody>props => {
const { url, ...rest } = props
const urlHere = `http://localhost:3000${url}`
return { url: urlHere, ...rest }
}debugbooleanfalsetruekneelMaker 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.
