An eco-system for infrastructure around REST API. It's worth noting that it is designed so that it can be used in all applications besides a REST API.
@apie/pipe
provides a way to create a pipe-function that has an event input. An event is considered a stateful object, that is passed through all functions, inspired by SvelteKits RequestEvent
. This provides dependency injection and access to important information for each eventful trigger.
That means, this is useful for any serverless-functions, such as AWS lambdas etc.
Option | Description | Type |
---|---|---|
before? | Functions that will be run before the pipeline | MaybeArray<(event: T) => unknown> |
after? | Functions that will be run before the pipeline (does not run if an exception is thrown) |
MaybeArray<(event: T, result: unknown) => unknown> |
finally? | Functions that will be run before the pipeline | MaybeArray<(event: T, result?: unknown, error?: unknown) => unknown> |
catch? | A function that returns a response in relation to an error being thrown (default: throw error ) |
catch?(event: T, error: unknown): APIResponse |
A pipe a function that accepts an array functions. These functions may look like
(state: T, input: any) => any
The state/event defines requirements. An example of such a state/event is SvelteKit's RequestEvent We define the state upon creating the pipe:
import { createEventPipe } from '@apie/pipe'
type State = {
value: number
}
const pipe = createEventPipe<State>()
Now each function inside the pipe will have access to State
:
const pipeline = pipe(
(state) => {
console.log(state.value)
return state.value * 2
},
(state, input) => {
console.log(input)
return input * 2
}
)
const result = pipeline({ value: 2 })
// result: 2 * 2 * 2 = 8
For consistency, you can create functions by using your pipe with just one function
const double = pipe((state) => state.value * 2)
double({ value: 2 }) // 4
If we want to multiple it by an arbitrary number, we can define the input:
const multiply = pipe((state, input: number) => state.value * input)
multiply({ value: 5 }, 2) // 10
Our pipelines are designed around HTTP Responses. That means, if you EVER return an HTTP Response using @apie/responses
, then the pipeline will exit early, resulting in that response.
import { OK } from '@apie/responses'
const fn = pipe(
() => {
return OK('Hi mom')
},
() => {
// This will never run
while(true) {}
}
)
const result = fn({ value: 2 }) // OK<'Hi mom'>
Sometimes we might want to re-use variables across multiple operations within our pipe.
In this example we use (e,v) => pipe(...)
to access the variable v
, within our pipe function.
const Input = z.object({...})
const $bulkWriter = createBulkWriter(...)
const pipeline = pipe(
zodValidator(Input),
(e, v) => arrayChanges(
v.existingDocuments,
v.updatedDocuments,
t => t._id?.toString()
),
(e, v) => pipe(
v.newEntries,
$bulkWriter.insertMultiple,
v.removedEntries.map(v => v._id),
$bulkWriter.updateMany(
v => ({
filter: { _id: { $in: v } },
update: { _status: 'archived' }
})
),
v.persistedEntries,
$bulkWriter.updateMultiple(
v => ({
filter: { _id: v._id },
update: { name: v.name }
})
)
),
$bulkWriter.execute()
)
Okay, let's say you want to re-use a result, but … we can't really declare variables here, can we?
Introducing… saveResult
import { saveResult } from '@apie/pipe'
const multiply = pipe((state, input: number) => state.value * input)
const [$multiply, get$multiply] = saveResult(multiply)
const pipeline = pipe(
2,
$multiply,
(state, input: number) => {
// ...
},
// ... Functions later
(state) => OK(get$multiply(state))
)
const result = pipeline({ value: 3 }) // OK<number> -> body: 6
The result is saved within the state
, so by referencing the state later, we can get access to the value.
If you use $multiply
again, then it returns the past result it resolved, rather than re-initializing.