简体   繁体   中英

Why does it throw an error cannot read property of undefined (reading 'length') when running a GraphQL Subscription with Redis?

Overview

  • Working on a NestJS project with GraphQL using a laptop with Window OS
  • Experimenting with GraphQL Subscriptions using graphql-redis-subscription@2.5.0 package
  • Redis is used in a docker container, see the docker-compose.yml below
  • The problem arose when the subscription postAdded is executed in GraphQL Playground. Instead of hanging to listen for events, it had crashed before I performed createPost mutation.

My code (I only include some important details)

posts.resolver.ts

import { Inject, UseGuards } from '@nestjs/common';
import { Args, Context, Mutation, Resolver, Subscription } from '@nestjs/graphql';
import { RedisPubSub } from 'graphql-redis-subscriptions';
import { GraphqlJwtAuthGuard } from '../auth/guards';
import { RequestWithUser } from '../auth/interfaces';
import { PUB_SUB } from '../pubsub/pubsub.module'

const POST_ADDED_EVENT = 'postAdded';

@Resolver(() => Post)
export class PostsResolver {
  constructor(
    private postsService: PostsService,
    @Inject(PUB_SUB) private pubSub: RedisPubSub,
  ) {}

  // my subscription (issue)
  @Subscription(() => Post)
  postAdded() {
    return this.pubSub.asyncIterator(POST_ADDED_EVENT);
  }

  // createPost method
  @Mutation(() => Post)
  @UseGuards(GraphqlJwtAuthGuard)
  async createPost(
    @Args('input') createPostInput: CreatePostInput,
    @Context() context: { req: RequestWithUser },
  ) {
    // just create a new post (assuming it works)
    const newPost = await this.postsService.create(
      createPostInput,
      context.req.user,
    );
    this.pubSub.publish(POST_ADDED_EVENT, { postAdded: newPost });
    return newPost;
  }
}

pubsub.module.ts

import { ConfigService } from '@nestjs/config';
import { RedisPubSub } from 'graphql-redis-subscriptions';
import { Global, Module } from '@nestjs/common';

export const PUB_SUB = 'PUB_SUB';

@Global()
@Module({
  providers: [
    {
      provide: PUB_SUB,
      useFactory: (configService: ConfigService) =>
        new RedisPubSub({
          connection: {
            host: configService.get('REDIS_HOST'),
            port: configService.get('REDIS_PORT'),
          },
        }),
      inject: [ConfigService],
    },
  ],
  exports: [PUB_SUB],
})
export class PubSubModule {}

app.module.ts

import { PubSubModule } from './pubsub/pubsub.module';

@Module({
  imports: [
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      playground: true,
      autoSchemaFile: path.join(process.cwd(), 'src/schema.gql'),
      installSubscriptionHandlers: true,
    }),
    PubSubModule,
    ConfigModule.forRoot({
      isGlobal: true,
      validationSchema: Joi.object({
        REDIS_HOST: Joi.string().required(),
        REDIS_PORT: Joi.number().required()
      }),
    }),
  ],
  providers: [AppService, AppResolver],
})
export class AppModule {}
version: '3'
services:
  redis:
    image: 'redis:alpine'
    ports:
      - '6379:6379'

  redis-commander:
    image: rediscommander/redis-commander:latest
    environment:
      - REDIS_HOSTS=local:redis:6379
    ports:
      - '8081:8081'
    depends_on:
      - redis

All the environment variables have already been defined in .env file.

REDIS_HOST="localhost"
REDIS_PORT=6379

When I run yarn start:dev and execute the subscription in GraphQL Playground

subscription {
  postAdded {
    id
    title
    paragraphs
  }
}

it raises an error like this:

{
  "errors": [
    {
      "message": "Cannot read properties of undefined (reading 'length')",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "postAdded"
      ]
    }
  ]
}

The terminal that monitors NestJS also raises an error like this:

[Nest] 8080  - 07/21/2022, 9:30:24 AM   ERROR [ExceptionsHandler] Cannot read properties of undefined (reading 'length')
TypeError: Cannot read properties of undefined (reading 'length')
    at JavascriptRedisParser.execute (C:\Users\HP\nestjs-project\node_modules\redis-parser\lib\parser.js:530:38)
    at Object.data (C:\Users\HP\nestjs-project\node_modules\ioredis\built\DataHandler.js:25:20)  
    at TransformOperationExecutor.transform (C:\Users\HP\nestjs-project\node_modules\src\TransformOperationExecutor.ts:207:39)
    at TransformOperationExecutor.transform (C:\Users\HP\nestjs-project\node_modules\src\TransformOperationExecutor.ts:327:31)
    at TransformOperationExecutor.transform (C:\Users\HP\nestjs-project\node_modules\src\TransformOperationExecutor.ts:327:31)
    at TransformOperationExecutor.transform (C:\Users\HP\nestjs-project\node_modules\src\TransformOperationExecutor.ts:327:31)
    at TransformOperationExecutor.transform (C:\Users\HP\nestjs-project\node_modules\src\TransformOperationExecutor.ts:327:31)
    at TransformOperationExecutor.transform (C:\Users\HP\nestjs-project\node_modules\src\TransformOperationExecutor.ts:327:31)
    at ClassTransformer.instanceToPlain (C:\Users\HP\nestjs-project\node_modules\src\ClassTransformer.ts:25:21)
    at Object.classToPlain (C:\Users\HP\nestjs-project\node_modules\src\index.ts:23:27)

I have installed all the necessary dependencies like ioredis , graphql-redis-subscriptions and even graphql-subscriptions but the errors still exist. Redis also seems to be running properly.

I have tried reading the error logs but it did not occur in my source code and doing some research on StackOverFlow but none seems to have solved the problem.

are you by any chance using a global ClassSerializerInterceptor?? Because I was running into the same problem just today and I solved by removing the interceptor. It happened because the subscription needs to receive an instance of AsyncIterable but the class serializer turns it into a plain object.

Apart from that I would recommend you change the GraphQl config, remove the installSubscriptionHandlers and change the config like this:

GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      playground: true,
      autoSchemaFile: path.join(process.cwd(), 'src/schema.gql'),
      // remove this option: 
      // installSubscriptionHandlers: true,
      // add the following:
      subscriptions: {
           "graphql-ws": true // or config object
      }
    }),

you can read more about it in the nestjs docs

I hope this solves your problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM