PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Monday, September 5, 2022

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

 September 05, 2022     graphql, nestjs, node.js, redis, typescript     No comments   

Issue

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.


Solution

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.



Answered By - Fran Messina
Answer Checked By - Marie Seifert (PHPFixing Admin)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing