GraphQL Middleware Integration
Relevant source files
The following files were used as context for generating this wiki page:
The GraphQL Middleware Integration in @joindeed/prisma-auth provides a declarative authorization layer that operates at the Prisma model level rather than the resolver level. It secures GraphQL APIs by intercepting requests and applying constraints based on @Auth annotations defined in the Prisma schema. This approach prevents security vulnerabilities where deep relations in GraphQL queries might inadvertently expose restricted data.
The system integrates with graphql-middleware to apply three distinct types of authorization checks: field-level constraints, type-level constraints for one-to-one relations, and list-level constraints that inject Prisma where and select clauses.
Sources: README.md:3-12, src/index.ts:37-41
Core Architecture
The integration consists of three primary middleware functions generated by makeAuthorizationMiddlewares. These middlewares use a configuration object to match roles defined in the Prisma schema to logic defined in JavaScript/TypeScript.
The diagram shows how requests flow through specialized middlewares to enforce security before data reaches the user.
Configuration Structure
The Configuration object defines how roles are matched globally or per Prisma model type.
| Property | Type | Description |
|---|---|---|
globalRoles | Record<string, Role> | Role matchers applied to all Prisma models. |
rolesPerType | RolesPerType | Role matchers specific to a Prisma model name. |
defaultFields | Record<string, object | function> | Fields always selected when a specific model is requested. |
dmmf | any[] | Optional Prisma DMMF for model metadata resolution. |
Sources: src/index.ts:14-30, README.md:65-80
Authorization Middleware Types
Field Constraint Middleware
makeFieldConstraintMiddleware enforces authorization on individual fields. It reads the @Auth annotation from the field description in the Prisma schema. If a user does not meet the role requirements for a specific field, the middleware patches the response to null, an empty string, or an empty array depending on the field's return type.
Sources: src/makeFieldConstraintMiddleware.ts:16-52
Type Constraint Middleware
makeTypeConstraintMiddleware secures one-to-one relations. When a field returns a specific Prisma model type, this middleware checks the @Auth annotations at the model level. If the matcher for the required role returns false, the relation field is set to null.
Sources: src/makeTypeConstraintMiddleware.ts:28-66
List Constraint Middleware
makeListConstraintMiddleware handles list queries and nested relation lists. It injects a withAuth function into the GraphQL context. Developers must use context.withAuth() when calling Prisma to apply generated where and select constraints automatically.
Sources: src/makeListConstraintMiddleware.ts:25-78
Data Flow and Selection Logic
The integration uses the PrismaSelect class to transform GraphQL info objects into Prisma-compatible selection objects. It recursively traverses the GraphQL query and merges security constraints.
The sequence illustrates how withAuth acts as a bridge between the resolver and the security-enhanced selection logic.
Dependency Management
Role matchers often require specific fields to be present in the database record (e.g., an ownerId to check against currentUser.id). The system uses matcherDependenciesSelect to ensure these fields are always included in the Prisma select clause, even if the GraphQL user did not request them.
Sources: src/getMatcherDependenciesSelect.ts:11-35, src/select.ts:200-210
Query Constraints
For lists, the queryConstraint function in the role definition generates a Prisma where clause. These clauses are wrapped in an AND block containing an OR of all applicable role constraints to ensure user-provided filters do not bypass security.
Sources: src/getListWhereConstrains.ts:32-72
Implementation Procedure
- Annotate Schema: Add
/// @Auth(read: [RoleName])comments to Prisma models or fields. - Define Configuration: Create a
Configurationobject withmatcher,queryConstraint, andmatcherDependenciesSelectfor each role. - Apply Middleware: Use
applyMiddlewarefromgraphql-middlewareto wrap the GraphQL schema withmakeAuthorizationMiddlewares(config). - Wrap Prisma Calls: Within resolvers, wrap the Prisma argument object with
context.withAuth().
// Example resolver usage
resolve: async (parent, args, context) => {
return context.prisma.purchases.findMany(
context.withAuth({
where: { status: 'COMPLETED' }
})
);
}
Sources: README.md:85-101, src/index.ts:37-41
Summary
GraphQL Middleware Integration in this project shifts authorization from imperative logic in resolvers to a declarative system based on the Prisma schema. By using makeFieldConstraintMiddleware, makeTypeConstraintMiddleware, and makeListConstraintMiddleware, the system ensures that every entry point and relation in the GraphQL graph is protected by consistent role-based access controls. The use of withAuth and PrismaSelect automates the complex task of merging user queries with security filters and required data dependencies.