Extending the GraphQL API
This guide explains how to add new queries, mutations, types, and permissions to the GraphQL API.
Architecture Overview
The GraphQL implementation is split between two packages:
| Package | Purpose |
|---|---|
packages/shared-graphql/ | Schema definitions (.graphql files), generated types, authorization helpers |
apps/web/src/lib/graphql/ | Resolvers, server configuration, Apollo Client |
Adding a New Domain
To add a new domain (e.g., project/):
1. Create the Schema
Create packages/shared-graphql/src/schemas/project/schema.graphql with type definitions, inputs, responses, and query/mutation extensions.
2. Update Codegen Configuration
Add the schema path to packages/shared-graphql/codegen.ts in the schema array.
3. Generate Types
Run nx generate shared-graphql to regenerate TypeScript types.
4. Update Schema Loader
Add the new schema to apps/web/src/lib/graphql/schema.ts.
5. Create Resolvers
Create apps/web/src/lib/graphql/project/resolvers.ts with resolver implementations.
6. Register Resolvers
Add resolvers to apps/web/src/lib/graphql/resolvers.ts.
Adding a New Type
- Define TypeScript type in
packages/shared-types/src/lib/core.ts - Add GraphQL type to appropriate
.graphqlfile inpackages/shared-graphql/src/schemas/ - Run
nx generate shared-graphqlto regenerate types using GraphQL Code Generator - Implement resolvers if needed
Adding a New Query
- Add query definition to the appropriate
.graphqlschema file usingextend type Query - Run
nx generate shared-graphql - Implement resolver in the domain's
resolvers.ts - Register in main
resolvers.ts
Adding a New Mutation
- Add mutation definition to the appropriate
.graphqlschema file usingextend type Mutation - Run
nx generate shared-graphql - Implement resolver with input validation
- Register in main
resolvers.ts
Adding a New OAuth Scope
1. Update Scope Type
Add the new slug to the Permission union in packages/shared-types/src/lib/core.ts, then create it in the WorkOS dashboard and attach it to the roles that should hold it (mirroring ROLE_PERMISSIONS).
2. Update Default Scopes (Optional)
There is no default-grant list: a permission reaches a session only because WorkOS signed it into the access token. Keep slugs short — they ride in the session cookie.
3. Use in Resolvers
Use requirePermission(context, "your:permission") in resolver implementations.
NX Commands
| Command | Description |
|---|---|
nx generate shared-graphql | Generate TypeScript types from GraphQL schemas |
nx build shared-graphql | Build the shared-graphql package |
nx lint shared-graphql | Lint the shared-graphql package |
nx test web | Test the web app (includes GraphQL resolver tests) |
Best Practices
- Keep schemas in shared-graphql - Schema definitions should be in
packages/shared-graphql/src/schemas/ - Use
.graphqlfiles - Keep schema definitions in.graphqlfiles for better tooling - Run generate after schema changes - Always run
nx generate shared-graphqlafter modifying schemas - Use response wrappers - Return
{ data, error }objects instead of throwing errors - Validate inputs - Check all input fields and return clear error messages
- Authorize explicitly - Call
requirePermission(); the@authdirective is documentation only - Use database repositories - Put database logic in
packages/shared-db, not resolvers
Reference Files
| File | Purpose |
|---|---|
packages/shared-graphql/src/types.ts | Context type and authorization helpers |
packages/shared-graphql/codegen.ts | Code generation configuration |
apps/web/src/lib/graphql/schema.ts | Schema loader |
apps/web/src/lib/graphql/resolvers.ts | Combined resolvers |
apps/web/src/lib/graphql/common/utils.ts | Helper functions |