Row Security
The withRowSecurity and mergeHooks utilities let you implement row-level security using the hooks system. Instead of writing before hooks manually for every operation, you define declarative rules that automatically inject WHERE clauses.
withRowSecurity
Section titled “withRowSecurity”Converts a map of table-level security rules into a HooksConfig. Each rule is a function that receives the resolver context and returns a filter object. The filter is injected as a WHERE clause on all read and write operations that accept a where argument (query, querySingle, count, update, delete).
import { withRowSecurity } from '@graphql-suite/schema'
const securityHooks = withRowSecurity({ post: (context) => ({ authorId: { eq: context.user.id } }), comment: (context) => ({ postAuthorId: { eq: context.user.id } }),})The generated hooks apply to the following operations per table:
queryquerySinglecountupdatedelete
Security rules override user-supplied filters on conflicting keys. If a user passes where: { authorId: { eq: 'other' } } but the security rule sets authorId: { eq: context.user.id }, the security rule wins.
mergeHooks
Section titled “mergeHooks”Combines multiple HooksConfig objects into one. This is useful when you have row security hooks, logging hooks, and application-level hooks that need to coexist.
import { mergeHooks, withRowSecurity } from '@graphql-suite/schema'
const securityHooks = withRowSecurity({ post: (context) => ({ authorId: { eq: context.user.id } }),})
const loggingHooks = { post: { insert: { after: async (ctx) => { console.log('Post inserted:', ctx.result) return ctx.result }, }, },}
const allHooks = mergeHooks(securityHooks, loggingHooks)
const config = { hooks: allHooks,}Merge behavior
Section titled “Merge behavior”beforehooks are chained sequentially. Each hook receives the modifiedargsfrom the previous hook.afterhooks are chained sequentially. Each hook receives the modifiedresultfrom the previous hook.resolvehooks cannot be composed. The lastresolvehook wins.
You can pass undefined values to mergeHooks safely; they are ignored.
Full example
Section titled “Full example”import { buildSchema, mergeHooks, withRowSecurity } from '@graphql-suite/schema'
const securityHooks = withRowSecurity({ post: (context) => ({ organizationId: { eq: context.user.orgId } }), document: (context) => ({ teamId: { eq: context.user.teamId } }),})
const applicationHooks = { post: { query: { before: async (ctx) => { return { args: { ...ctx.args, where: { ...ctx.args?.where, status: { eq: 'published' } } } } }, }, },}
const { schema } = buildSchema(db, { hooks: mergeHooks(securityHooks, applicationHooks),})In this example, a post query receives both the security filter (organizationId) and the application filter (status: 'published'). The security hook runs first, then the application hook chains on top.