0.1.18 • Published 4 months ago

kneel v0.1.18

Weekly downloads
-
License
Unlicense
Repository
github
Last release
4 months ago

kneel

Fetch with Zod.

Installation

npm install kneel

Usage

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'

Problem

Making validation functional can require patterns like annotating with unknown.

Without kneel

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)
}

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({ 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
  })
}

Parameters

url
string
'http://localhost:3000'
i
ZodSchema
z.object({
  input: z.string()
})
o
ZodSchema
z.object({
  output: z.number()
})
body
z.infer<typeof i>

If i is set

{ input: 'hello' }
method
'GET'
| 'POST'
| 'PUT'
| 'DELETE'
| 'PATCH'

'GET', 'POST' if i is set

'PUT'
headers
HeadersInit
{ 'x-api-key': 'token' }
encoding
'application/x-www-form-urlencoded'
| 'multipart/form-data'
| 'text/plain'
| 'application/json'
'application/json'

if i is set

'text/plain'
debug
boolean
false
true

kneel takes a single object with one required parameter:

  • url, a string

You can optionally set:

  • method, a string
  • headers, matching the native fetch headers

Output

You can optionaly set an output schema with:

  • o, a zod 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.

Input

You can optionally include a request payload with:

  • i, a zod schema
  • body, a value matching the i schema

The request body will be parsed by the i schema. By default including a body sets the method to 'POST'.

Encoding

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().

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 <
  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:

Input
{ input: string }
InputSchema
ZodSchema<Input>
ZodObject<{ name: ZodString }>
Output
void
{ output: 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'
  o: outputSchema 
})
console.log(response) // 'world' 
make
<
  Input,
  InputSchema extends ZodSchema<Input>,
  Output = void
> (
  props: KneelProps<Input, InputSchema, Output>
) => KneelProps<Input, InputSchema, Output>
props => {
  const { url, ...rest } = props
  const urlHere = `http://localhost:3000${url}`
  return { url: urlHere, ...rest }
}
debug
boolean
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.

0.1.18

4 months ago

0.1.17

4 months ago

0.1.16

5 months ago

0.1.15

5 months ago

0.1.14

5 months ago

0.1.13

5 months ago

0.1.12

7 months ago

0.1.11

7 months ago

0.1.10

7 months ago

0.1.9

7 months ago

0.1.8

7 months ago

0.1.7

7 months ago

0.1.6

7 months ago

0.1.5

7 months ago

0.1.4

7 months ago

0.1.3

7 months ago

0.1.2

7 months ago

0.1.1

7 months ago

0.1.0

7 months ago