1. Backend - Refzlund/sveltekit-zero-api GitHub Wiki
Endpoints are the URLs where we use our methods; GET, POST, PUT etc.
These can take a body or a query. Let's define our endpoint inputs:
interface Post = {
body: {
productName: string
},
query?: {
value?: number
}
}
query
and query.value
are marked as optional using '?'. (Note: This is not validated by ZeroAPI)
Inside our endpoint, we import import type { KitEvent } from 'sveltekit-zero-api'
. and we can optionally import RequestEvent
to get typed params
:
import type { KitEvent } from 'sveltekit-zero-api'
import type { RequestEvent } from './$types'
export async function POST(event: KitEvent<Post, RequestEvent>) {
}
An endpoint (ex. api.product.post
) must return a response.
Due to how Sveltekit Zero API works, it must be a type returned from sveltekit-zero-api/http
. (All generalized HTTP responses are covered)
import type { KitEvent } from 'sveltekit-zero-api'
import type { RequestEvent } from './$types'
import { Ok, BadRequest, InternalError } from 'sveltekit-zero-api/http'
interface Post = {
body: {
productName: string
}
}
export async function POST(event: KitEvent<Post, RequestEvent>) {
// We get our typed body:
const { productName } = await event.request.json()
if(ok)
return Ok({ body: { message: 'Product posted' }})
return BadRequest()
}
Sometimes you might want to type your endpoint method response in relation to the input. You can reference the input request as a type via generics:
interface Post = {
body: {
productName: string
}
}
export async function POST<const Request extends Post>(event: KitEvent<Request, RequestEvent>) {
// We get our typed body:
const { productName } = await event.request.json()
const data = db.getProduct({ name: productName })
if(data)
return Ok({
body: {
productName: productName as Request['body']['productName'],
data
}
})
return BadRequest()
}
Example of what that would look like
API Endpoint:Frontend:
A handy utility function is querySpread
— it will make an object of your queries, and attempt to format into their respective types.
import type { KitEvent } from 'sveltekit-zero-api'
import type { RequestEvent } from './$types'
import { querySpread } from 'sveltekit-zero-api'
interface Post = {
query: {
age: number
name: string
permitted: boolean
parents: {
mother: name
father: name
}
}
}
export async function POST(event: KitEvent<Post, RequestEvent>) {
// We get our typed body:
const {
age,
name,
permitted,
parents: {
mother = "Not provided.", // Default value, if value is undefined
father = "Not provided."
} = {} // Default value of 'parents'
} = querySpread(event)
...
}
If these queries are present, they can be accessed with or without a deconstructor. The querySpread
by default, formats the values using a formatting determination algorithm. This does not validate the specific type, so remember to do type checking. You can opt-out on the formatting using second parameter; querySpread(event, false)
"abc" => "abc"
"123.12" => 123.12 // Only contains numbers
"$123.123" => "$123.123" // NaN
"123.12.12" => "123.12.12" // NaN
"true" => true
"TRUE" => "TRUE" // Booleans has to be lowercase
"false" => false
"undefined" => undefined
"null" => null
"NULL" => "NULL" // `null` and `undefined` has to be lowercase
"{...}": => {...}
"[...]" => [...]
"2022-05-06T22:15:11.244Z" => new Date("2022-05-06T22:15:11.244Z") // Only accepts ISO-date strings (i.e. `new Date().toISOString()`)
'"2022-05-06T22:15:11.244Z"' => new Date("2022-05-06T22:15:11.244Z") // Has quotes around the ISO-string (from `new Date()`)
Warning This is deprecated and will be removed in 1.0.0.
Instead, please make use of endpointPipe
Handling errors in backend can be typed using another utility function of SvelteKit Zero API: err
... yeah just err.
import type { KitEvent } from 'sveltekit-zero-api'
import type { RequestEvent } from './$types'
import { querySpread, err } from 'sveltekit-zero-api'
export async function POST(event: KitEvent<Post, RequestEvent>) {
const { message } = await event.request.json()
const query = querySpread(event)
// We can't refer `const query` directly to `err.require`,
// since if query.boink is undefined, it will not be in the object in the first place.
const { boink, test } = query
// We define variable for our `err.handler`
let errorResponse = err.handler(
// Requires these values to be present
err.require({ boink, test, message }),
// Requires values of object query, to have these types (via typeof)
err.type(query, { boink: 'string', test: 'number' }),
// (0) If a condition is failed, will result in the (1) error message:
err.test(boink?.length > 2, { boink: 'Must be longer than 2 characters' }),
// It matches (0) each value of each key, to the (1) Regex expression, and (2) errors if it fails:
err.match({ message }, /Giraffe/g, 'Must include the word "Giraffe"')
)
if (errorResponse)
// if `errorResponse` exists, then we can return it with a parameter of the status we'd return:
return errorResponse('BadRequest')
...
The front-end response will be a BadRequest, with
{
body: {
errors: {
boink?: string
message?: string
test?: string
},
message?: string
}
}
The error properties are optional, since they only may error. If a key
(ex. boink
), fails multiple times, the first one will always show. boink
will therefore show "Required"
, before "Must be longer than 2 characters"
.
This object is a collection of functions to handle errors.
If there are any record objects in the arguments, it will result in an error-response.
Functions will therefore return either an object or null.
Example This will always result in an error:
err.handler({ name: 'Name was not specified' })
Takes in an object, and checks whether each item is undefined or null.
Will check each key of obj, to see if it matches the assigned type.
If condition fails, will result in the errors specificed
Tests each value of obj of a RegExpression, if it fails, it will result in the specified error.