Skip to main content
← All notes
Building

One guard function protects every admin write operation

software

Every server action in the admin panel starts with the same four lines. Call requireAdmin. Get the Clerk session. Fetch the user. Check publicMetadata.role. If the role is not admin, throw before any database write runs. That is the entire authorisation model for the admin side of the portal. Seven server actions — createClient, createProject, addSessionNotes, updateMilestone, createMilestone, createDeliverable, createActionItem, createGoal, addAdminComment — all call the same function on line one. No decorator pattern, no middleware chain, no role-checking wrapper component. Just a plain async function that throws if you are not me. The client-side server actions use a different pattern. They call auth from Clerk, look up the client by clerkId, and verify ownership of the specific resource before writing. The toggleActionItem action goes further — it checks that the action item is assigned to client before allowing the toggle. A client cannot complete my tasks. I cannot accidentally run a client action because the client actions check clerkId, not role. Two guard patterns, ten server actions, zero unprotected writes. The admin guard is four lines. The client guard is three queries. Both throw before touching the database if anything is wrong. Security is not a feature you bolt on at the end. It is the first line of every function.

Comments coming soon

Sign in with TikTok to leave a comment. Coming soon.