3. Endpoint Pipe function - Refzlund/sveltekit-zero-api GitHub Wiki
import { endpointPipe, type KitEvent } from 'sveltekit-zero-api'
import { Ok, Unauthorized, BadRequest } from 'sveltekit-zero-api/http'
import { authGuard, parseJSON } from '$endpoint-services'
interface Post {
body: {
foo: string
}
}
export async function POST<const R extends Post>(event: KitEvent<Post>) {
return endpointPipe(event) (
authGuard('admin'),
parseJSON<Post>, // Parses JSON in a try-catch block
(event, locals: { something: string }) => {
const { foo } = event.locals.json
// ^ Typed via parseJSON<Post>
locals.something = 'Helloo'
return Ok()
},
(event) => {
event.locals.something
// ^ Is now typed
console.log('This function won\'t run, because the previous definitively returns a response')
}
)
}
This pipe function promotes flexible code-splitting, reusability, and usage of the SvelteKit event.locals
by keeping it typed based on previous run functions!
The pipe takes in event and an optional default-fallback response:
endpointPipe(event, () => BadRequest({ body: { message: 'End of pipe:(' } }))
It returns another function, and this function accepts our pipe-line functions. An example of such a function could be:
endpointPipe(event)(
(event) => {
return Ok()
}
)
This function has 2 arguments; (event, locals) => {...}
.
You type event
to set requirements to both JSON body/query AND the event.locals
.
You type locals
to update the event.locals
for functions coming after.
interface UpdateUserName {
body: {
name: string
},
query: {
age: number
}
}
function updateUserName(event: KitEvent<UpdateUserName>) {
Notice it's inside KitEvent<...>
just like with endpoints. Same concept.
function updateUserName(event: KitEvent & { locals: { user: { name: string } } }) {
Here we use &
to extend the event. This also sets a type requirement.
To type locals, you use the second parameter in a function:
function getUserName(
event: KitEvent & { locals: { json: { name: string } } },
locals: { name: string }
) {
locals.name = event.locals.json.name
// ^ These are the same object ^
}
How many times do you use await event.request.json()
? Every time... Pretty much.
If the endpoint receives an invalid JSON format, and you don't try-catch block it — then it will throw an InternalServerError (500). This is obviously not a server error, and should return 400.
Let's create a pipeline function!
// parse-json.ts
export async function parseJSON<T extends { body: Record<any, any> | any[] }>(
event: KitEvent<T>,
locals: { json: T['body'] }
) {
try {
if(event.request.bodyUsed)
throw new Error('Body has already been read!') // An actual server error.
locals.json = await event.request.json()
} catch (error) {
return BadRequest({
body: {
message: 'Bad JSON in request body'
}
})
}
}
We type locals: {}
to update it for functions running after. We use a TypeScript Generic to know what the contents are for the JSON.
And in the function itself, we only return a response if an error occurs.
import { parseJSON } from '$endpoint-services'
export async function POST(event: KitEvent<Post>) {
return endpointPipe(event) (
parseJSON<Post>,
({ locals }) => {
// Do stuff with `event.locals`
return Ok({...})
}
)
}