Merge Multiple Procedures into One in ZSA
tusheer opened this issue · comments
I am new to using ZSA and would like to know if there is a way to merge multiple procedures into a single procedure when creating an action.
Hi, you can chain procedures. This is a sequential merge of procedures. If you are looking for something else, please provide a code example of what you want the code to be able to do with regard to merging. Happy to help out.
Let's say we have four procedures: authProcedure
, logProcedure
, rateLimitProcedure
, and another procedure. Each procedure is independent of the others. How can we use more than two procedures in a single action?
Hi, I will go over a few things I would do:
(1) if these procedures can just be standalone functions, call functions from onStart event
const myAction = protectedAction
.onStart(async () => {
// call functions here
await Promise.all([checkRateLimit(), log()])
})
.handler(async ({ ctx }) => {
return `hello world ${ctx.auth.name}`
})
note: you can even just call these in the handler itself and not use onStart
(2) Use a procedure builder
const buildAction = (run: ("rate-limit" | "log")[]) => {
const procedure = createServerActionProcedure().handler(async () => {
const session = await auth()
if (run.includes("rate-limit")) {
await checkRateLimit(session.id)
}
if (run.includes("log")) {
await log(session.id)
}
return session
})
return procedure.createServerAction()
}
const myAction = buildAction(["rate-limit", "log"]).handler(async ({ ctx }) => {
return `hello world ${ctx.name}`
})
I have been personally using these light weight procedure builder functions in my code base and have found them quite useful. I'm sure there are many ways to do this.
Would something like this work for you? The real magic of ZSA
is the chaining of dependent procedures as we take care of context flows and input merging. However, if the procedures are independent (which I assume means no input and no context), I believe just calling them as normal functions -- within procedures/actions -- is the easiest path.
Hopefully that makes sense. If I misunderstood your situation, lmk and will discuss further.
We're using a similar approach @IdoPesok. Something like this so we can get Newrelic tracing on things too
export function createAuthAction(
name: string,
{ role }: { role?: 'user' | 'superuser' } = {}
) {
const authAction = baseAction.handler(async () => {
try {
const user = await currentUser()
if (role === 'superuser' && !(await getIsSuperUser(user.uid))) {
throw new Error('User is not a superuser')
}
return { user }
} catch {
throw new Error('User not authenticated')
}
})
return authAction.createServerAction().onStart((props) => {
newrelic.setTransactionName(`serverAction: ${name}`)
logger.debug({ props, module: 'server-action' }, 'createAction start')
})
}
const createTodoAction = createAuthAction('createTodo', { role: 'superuser' })
Awesome @drewhamlett! I will probably add something about this pattern in the docs soon, so more people are aware.