Field-Level Authorization
Relevant source files
The following files were used as context for generating this wiki page:
Field-level authorization provides granular control over individual record attributes within a Prisma model. It ensures that sensitive data, such as email addresses or internal IDs, is only exposed to users who meet specific role requirements. This mechanism operates as a declarative layer using Prisma schema comments to define access rules directly on model fields.
Sources: README.md:1-15, src/makeFieldConstraintMiddleware.ts:7-16
Overview of Field Constraints
Authorization rules are defined in the Prisma schema using triple-slash comments (///). The middleware parses these comments to identify which roles have read access to a specific field. If a field contains an @Auth annotation, the system adopts a whitelist approach: access is denied unless the user matches at least one of the specified roles. Fields without @Auth annotations remain publicly accessible by default.
Sources: README.md:31-35, src/makeFieldConstraintMiddleware.ts:37-45
Authorization Flow
The following diagram illustrates how the makeFieldConstraintMiddleware processes a request for a specific field:
The middleware intercepts the resolver, checks the field's metadata, and validates the current user's context against the required roles.
Sources: src/makeFieldConstraintMiddleware.ts:21-55
Role Matching and Dependencies
Roles are validated using matcher functions defined in the global configuration or specifically for a model type. A matcher receives the current context, the record data, and any arguments provided in the @Auth annotation.
Data Requirements
If a matcher requires specific fields from the record to perform its logic (e.g., comparing record.userId to context.currentUser.id), these fields must be declared in matcherDependenciesSelect. The PrismaSelect utility automatically merges these dependencies into the underlying Prisma query to ensure the data is available when the middleware runs.
Sources: src/index.ts:10-12, src/getMatcherDependenciesSelect.ts:11-30, src/select.ts:233-245
Configuration Structure
| Property | Type | Description |
|---|---|---|
matcher | Function | Returns a boolean indicating if the user has access to the record. |
queryConstraint | Function | Returns a Prisma where clause for list filtering. |
matcherDependenciesSelect | Object | Function | Defines fields needed by the matcher to function. |
Sources: src/index.ts:9-13, README.md:43-55
Response Patching
When access to a field is denied, the middleware does not throw a GraphQL error. Instead, it "patches" the response to a safe default value based on the field's return type. This prevents breaking the schema while hiding sensitive data.
| Return Type | Patched Value |
|---|---|
Nullable Type (e.g., String) | null |
Non-Nullable String (e.g., String!) | "" (Empty String) |
List Type (e.g., [String]!) | [] (Empty Array) |
Sources: src/makeFieldConstraintMiddleware.test.ts:80-125, src/makeFieldConstraintMiddleware.ts:57-59
Implementation Example
Field-level rules are defined in the Prisma schema and mapped to JavaScript logic in the middleware configuration.
Prisma Schema Definition
model User {
id String @id
/// @Auth(read:[ Owner, Admin ])
email String
}
Sources: README.md:31-35
Middleware Configuration
const config = {
globalRoles: {
Owner: {
matcherDependenciesSelect: (roleArgs) => ({ userId: true }),
matcher: (ctx, record) => ctx.currentUser?.id === record?.userId,
queryConstraint: (ctx) => ({ userId: ctx.currentUser?.id }),
},
},
};
Sources: README.md:41-55, src/makeFieldConstraintMiddleware.test.ts:39-47
Introspection and Internal Fields
The middleware explicitly ignores GraphQL introspection fields (fields starting with __) and types containing __. This ensures that tools like GraphiQL or Apollo Studio can still fetch the schema structure even when authorization is active.
Sources: src/makeFieldConstraintMiddleware.ts:23-31, src/makeFieldConstraintMiddleware.test.ts:140-153
Field-level authorization provides a declarative, secure-by-default layer that integrates directly with Prisma's selection and querying logic, reducing the risk of accidental data exposure in complex GraphQL graphs.
Sources: README.md:5-10