Subscriptions
type-graphql-query includes out of the box subscription support. When enabling subscriptions, automatic subscriptions are created for all CRUD operations, allowing clients to receive real-time updates when data changes.
What You’ll Learn
Section intitulée « What You’ll Learn »This guide will help you understand:
- ✅ How to enable subscriptions for DTOs
- ✅ Available subscription events
- ✅ How to filter subscriptions
- ✅ How to use custom PubSub providers
- ✅ How to enable/disable individual subscriptions
- ✅ Best practices for subscriptions
Overview
Section intitulée « Overview »When enabling subscriptions, the following subscriptions will be automatically created:
created{ObjectName}- published when thecreateOneorcreateManymutation is called.updatedOne{ObjectName}- published when theupdateOnemutation is called.updatedMany{ObjectName}- published whenupdateManymutation is called.deletedOne{ObjectName}- published when thedeleteOnemutation is called.deletedMany{ObjectName}- published whendeleteManymutation is called.upserted{ObjectName}- published when theupsertOneorupsertManymutation is called.
Enabling Subscriptions
Section intitulée « Enabling Subscriptions »You can enable subscriptions through the auto-generated resolver using buildSchema or buildTypeDefsAndResolvers or by manually defining your resolver.
In both cases you need to set the enableSubscriptions option to true.
In the below example the following subscriptions will be exposed.
createdProduct- published when thecreateOneorcreateManymutation is called.updatedOneProduct- published when theupdateOnemutation is called.updatedManyProducts- published whenupdateManymutation is called.deletedOneProduct- published when thedeleteOnemutation is called.deletedManyProducts- published whendeleteManymutation is called.upsertedProduct- published when theupsertOneorupsertManymutation is called.
Using buildTypeDefsAndResolvers
Section intitulée « Using buildTypeDefsAndResolvers »import { buildTypeDefsAndResolvers } from '@lib/type-graphql-query';import { ProductDTO } from './product/product.type';import { ProductInputDTO } from './product/product-input.type';import { ProductUpdateDTO } from './product/product-update.type';
const { resolvers, typeDefs } = await buildTypeDefsAndResolvers({ resolvers: [ { DTOClass: ProductDTO, CreateDTOClass: ProductInputDTO, UpdateDTOClass: ProductUpdateDTO, enableSubscriptions: true, }, ], // ... other options});Using buildSchema
Section intitulée « Using buildSchema »import { buildSchema } from '@lib/type-graphql-query';import { ProductDTO } from './product/product.type';import { ProductInputDTO } from './product/product-input.type';import { ProductUpdateDTO } from './product/product-update.type';
const schema = await buildSchema({ resolvers: [ { DTOClass: ProductDTO, CreateDTOClass: ProductInputDTO, UpdateDTOClass: ProductUpdateDTO, enableSubscriptions: true, }, ], // ... other options});Manual Resolver Definition
Section intitulée « Manual Resolver Definition »When manually defining your resolver you must also provide a readonly pubSub property. In this case the default PubSub implementation should be injected.
import { CRUDResolver } from '@lib/type-graphql-query';import { Resolver } from '@lib/type-graphql';import { PubSub } from 'graphql-subscriptions';import { ProductDTO } from './product/product.type';import { ProductInputDTO } from './product/product-input.type';import { ProductUpdateDTO } from './product/product-update.type';import { ProductService } from './product/product.service';
@Resolver()export class ProductResolver extends CRUDResolver(ProductDTO, { CreateDTOClass: ProductInputDTO, UpdateDTOClass: ProductUpdateDTO, enableSubscriptions: true,}) { constructor( readonly service: ProductService, readonly pubSub: PubSub, ) { super(service); }}Enabling/Disabling Individual Subscriptions
Section intitulée « Enabling/Disabling Individual Subscriptions »You also have the option to selectively enable or disable individual subscriptions.
In this example the updatedMany and deletedMany subscriptions are disabled.
Using buildTypeDefsAndResolvers
Section intitulée « Using buildTypeDefsAndResolvers »import { buildTypeDefsAndResolvers } from '@lib/type-graphql-query';import { ProductDTO } from './product/product.type';import { ProductInputDTO } from './product/product-input.type';import { ProductUpdateDTO } from './product/product-update.type';
const { resolvers, typeDefs } = await buildTypeDefsAndResolvers({ resolvers: [ { DTOClass: ProductDTO, CreateDTOClass: ProductInputDTO, UpdateDTOClass: ProductUpdateDTO, enableSubscriptions: true, update: { many: { enableSubscriptions: false } }, delete: { many: { enableSubscriptions: false } }, }, ], // ... other options});Manual Resolver Definition
Section intitulée « Manual Resolver Definition »import { CRUDResolver } from '@lib/type-graphql-query';import { Resolver } from '@lib/type-graphql';import { PubSub } from 'graphql-subscriptions';import { ProductDTO } from './product/product.type';import { ProductInputDTO } from './product/product-input.type';import { ProductUpdateDTO } from './product/product-update.type';import { ProductService } from './product/product.service';
@Resolver()export class ProductResolver extends CRUDResolver(ProductDTO, { CreateDTOClass: ProductInputDTO, UpdateDTOClass: ProductUpdateDTO, enableSubscriptions: true, update: { many: { enableSubscriptions: false } }, delete: { many: { enableSubscriptions: false } },}) { constructor( readonly service: ProductService, readonly pubSub: PubSub, ) { super(service); }}Filtering Subscriptions
Section intitulée « Filtering Subscriptions »The created, updatedOne, deletedOne, and upserted subscriptions all allow a Filter to be passed in to filter for events that match the criteria.
The filter you provide is the same type that is used when querying for records.
For example to filter for all created Products where the name starts with Chicken and can be sold, you could do the following:
subscription { createdProduct(input: { filter: { name: { like: "Chicken%" }, canBeSold: { is: true } } }) { id name canBeSold createdAt updatedAt }}Subscription Filter Input
Section intitulée « Subscription Filter Input »The subscription filter input includes:
filter- The filter to apply (same as query filters)search- Search term (if searchable fields are defined)locale- Locale for localized fieldswithDeleted- Include soft-deleted records
subscription { updatedOneProduct( input: { filter: { name: { like: "Chicken%" } }, locale: "fr", withDeleted: false } ) { id name canBeSold }}Custom PubSub Provider
Section intitulée « Custom PubSub Provider »You can override the default PubSub implementation by creating your own provider and providing it to type-graphql-query.
Below is an example provider using Redis.
import { pubSubToken } from '@lib/type-graphql-query';import { RedisPubSub } from 'graphql-redis-subscriptions';import Redis from 'ioredis';import { Provider } from '@nestjs/common';
export class RedisPubSubProvider { static provider(): Provider { return { provide: pubSubToken(), useValue: new RedisPubSub({ publisher: new Redis(), subscriber: new Redis(), }), }; }}In order to let type-graphql-query know about the provider you can set the pubSub option in your resolver configuration.
import { buildTypeDefsAndResolvers } from '@lib/type-graphql-query';import { ProductDTO } from './product/product.type';import { ProductInputDTO } from './product/product-input.type';import { ProductUpdateDTO } from './product/product-update.type';import { RedisPubSubProvider } from './redis-pub-sub.provider';
const { resolvers, typeDefs } = await buildTypeDefsAndResolvers({ resolvers: [ { DTOClass: ProductDTO, CreateDTOClass: ProductInputDTO, UpdateDTOClass: ProductUpdateDTO, enableSubscriptions: true, pubSub: RedisPubSubProvider.provider(), }, ], // ... other options});Subscription Events
Section intitulée « Subscription Events »The following events are published for each operation:
Created Events
Section intitulée « Created Events »- Event Name:
created{ObjectName} - Published When:
createOneorcreateManymutations are called - Payload: The created DTO(s)
subscription { createdProduct { id name canBeSold }}Updated One Events
Section intitulée « Updated One Events »- Event Name:
updatedOne{ObjectName} - Published When:
updateOnemutation is called - Payload: The updated DTO
subscription { updatedOneProduct(input: { filter: { canBeSold: { is: true } } }) { id name canBeSold }}Updated Many Events
Section intitulée « Updated Many Events »- Event Name:
updatedMany{ObjectName} - Published When:
updateManymutation is called - Payload: The updated DTOs
subscription { updatedManyProducts { id name canBeSold }}Deleted One Events
Section intitulée « Deleted One Events »- Event Name:
deletedOne{ObjectName} - Published When:
deleteOnemutation is called - Payload: The deleted DTO (partial)
subscription { deletedOneProduct(input: { filter: { canBeSold: { is: true } } }) { id name }}Deleted Many Events
Section intitulée « Deleted Many Events »- Event Name:
deletedMany{ObjectName} - Published When:
deleteManymutation is called - Payload: The deleted DTOs (partial)
subscription { deletedManyProducts { id name }}Upserted Events
Section intitulée « Upserted Events »- Event Name:
upserted{ObjectName} - Published When:
upsertOneorupsertManymutations are called - Payload: The upserted DTO(s)
subscription { upsertedProduct(input: { filter: { name: { like: "Chicken%" } } }) { id name canBeSold }}Complete Example
Section intitulée « Complete Example »Here’s a complete example showing how to set up subscriptions:
import { buildTypeDefsAndResolvers } from '@lib/type-graphql-query';import { ProductDTO } from './product/product.type';import { ProductInputDTO } from './product/product-input.type';import { ProductUpdateDTO } from './product/product-update.type';
const { resolvers, typeDefs } = await buildTypeDefsAndResolvers({ resolvers: [ { DTOClass: ProductDTO, CreateDTOClass: ProductInputDTO, UpdateDTOClass: ProductUpdateDTO, enableSubscriptions: true, // Optionally disable specific subscriptions update: { many: { enableSubscriptions: false } }, delete: { many: { enableSubscriptions: false } }, }, ], // ... other options});Best Practices
Section intitulée « Best Practices »-
Use Filtering Wisely
- Always use filters to reduce the number of events your clients receive
- Filters help reduce network traffic and improve performance
-
Choose the Right PubSub Implementation
- Use in-memory
PubSubfor development and single-server deployments - Use Redis
PubSubfor production multi-server deployments - Consider using other implementations (e.g., RabbitMQ, Kafka) for high-scale scenarios
- Use in-memory
-
Disable Unnecessary Subscriptions
- Only enable subscriptions you actually need
- Disable
manysubscriptions if you don’t need bulk operation notifications
-
Handle Errors Gracefully
- Implement error handling in your subscription resolvers
- Use try-catch blocks when processing subscription events
-
Monitor Subscription Usage
- Monitor the number of active subscriptions
- Set limits to prevent resource exhaustion
Quick Reference
Section intitulée « Quick Reference »| Subscription | Event | Published When |
|---|---|---|
created{ObjectName} |
Creation | createOne or createMany |
updatedOne{ObjectName} |
Single update | updateOne |
updatedMany{ObjectName} |
Bulk update | updateMany |
deletedOne{ObjectName} |
Single deletion | deleteOne |
deletedMany{ObjectName} |
Bulk deletion | deleteMany |
upserted{ObjectName} |
Upsert | upsertOne or upsertMany |
Next Steps
Section intitulée « Next Steps »Continue learning:
- 🔍 Queries - Learn about query endpoints
- ✏️ Mutations - Learn about mutation endpoints
- 🔎 Filtering - Understand filtering options
- 🔧 Resolvers - Learn about resolver implementation
Related Topics
Section intitulée « Related Topics »- Queries - Query endpoints
- Mutations - Mutation endpoints
- Filtering - Filtering options
- Resolvers - Resolver implementation
- API - Subscriptions - API subscriptions documentation