Extending the GraphQL API
This guide explains how to add new queries, mutations, types, and OAuth scopes 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, OAuth scope definitions |
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 scope to packages/shared-graphql/src/types.ts in the OAuthScope type.
2. Update Default Scopes (Optional)
If the scope should be granted to all authenticated users by default, add it to DEFAULT_SCOPES.
3. Use in Resolvers
Use requireScope(context, "your-scope:read") 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
- Use scopes for authorization - Prepare for OAuth by using
requireScope() - Use database repositories - Put database logic in
packages/shared-db, not resolvers
Reference Files
| File | Purpose |
|---|---|
packages/shared-graphql/src/types.ts | OAuth scopes and context types |
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 |