title: express-session crashes server on setting cookie?
body: <p>Basically this other post <a href="https://stackoverflow.com/questions/69333408/express-session-does-not-set-cookie">Express-session does not set cookie?</a> where I'm following Ben Awad's Fullstack Tutorial. The cookie gets created but the server crashes and this is the error</p>
  <pre><code>node:internal/errors:464
  ErrorCaptureStackTrace(err);
  ^
  
  TypeError [ERR_INVALID_ARG_TYPE]: The &quot;chunk&quot; argument must be of type string or an instance of Buffer or Uint8Array. Received an instance of Array
      at new NodeError (node:internal/errors:371:5)
      at _write (node:internal/streams/writable:312:13)
      at Socket.Writable.write (node:internal/streams/writable:334:10)
      at RedisSocket.writeCommand (/home/kuratar/github/milestone-4-Kuratar/server/node_modules/@node-redis/client/dist/lib/client/socket.js:57:130)
      at Commander._RedisClient_tick (/home/kuratar/github/milestone-4-Kuratar/server/node_modules/@node-redis/client/dist/lib/client/index.js:415:64)
      at Commander._RedisClient_sendCommand (/home/kuratar/github/milestone-4-Kuratar/server/node_modules/@node-redis/client/dist/lib/client/index.js:396:82)
      at Commander.commandsExecutor (/home/kuratar/github/milestone-4-Kuratar/server/node_modules/@node-redis/client/dist/lib/client/index.js:160:154)
      at Commander.BaseClass.&lt;computed&gt; [as set] (/home/kuratar/github/milestone-4-Kuratar/server/node_modules/@node-redis/client/dist/lib/commander.js:8:29)
      at RedisStore.set (/home/kuratar/github/milestone-4-Kuratar/server/node_modules/connect-redis/lib/connect-redis.js:65:21)
      at Session.save (/home/kuratar/github/milestone-4-Kuratar/server/node_modules/express-session/session/session.js:72:25)
      at Session.save (/home/kuratar/github/milestone-4-Kuratar/server/node_modules/express-session/index.js:406:15)
      at ServerResponse.end (/home/kuratar/github/milestone-4-Kuratar/server/node_modules/express-session/index.js:335:21)
      at ServerResponse.send (/home/kuratar/github/milestone-4-Kuratar/server/node_modules/express/lib/response.js:221:10)
      at /home/kuratar/github/milestone-4-Kuratar/server/node_modules/apollo-server-express/dist/ApolloServer.js:89:25 {
    code: 'ERR_INVALID_ARG_TYPE'
  }
  </code></pre>
  <p>I noticed that this specific line of code in user.ts:</p>
  <pre><code>req.session.userId = user.id
  </code></pre>
  <p>when it's commented out, the error doesn't occur but the cookie is not set. There isn't a set-cookie option in the response-header.
  My files are pretty much the same as this other person in the post I linked.</p>
  <p>index.ts</p>
  <pre><code>import &quot;reflect-metadata&quot;;
  import { MikroORM } from &quot;@mikro-orm/core&quot;;
  import { __prod__ } from &quot;./constants&quot;;
  import microConfig from &quot;./mikro-orm.config&quot;;
  import express from &quot;express&quot;;
  import { ApolloServer } from &quot;apollo-server-express&quot;;
  import { ApolloServerPluginLandingPageGraphQLPlayground } from &quot;apollo-server-core&quot;;
  import { buildSchema } from &quot;type-graphql&quot;;
  import { HelloResolver } from &quot;./resolvers/hello&quot;;
  import { PostResolver } from &quot;./resolvers/post&quot;;
  import { UserResolver } from &quot;./resolvers/user&quot;;
  import * as redis from &quot;redis&quot;;
  import session from &quot;express-session&quot;;
  import connectRedis from &quot;connect-redis&quot;;
  import { MyContext } from &quot;./types&quot;;
  
  // start postgresql server on wsl - sudo service postgresql start
  //    stop - sudo service postgresql stop
  // start redis server on wsl - redis-server
  //    sudo /etc/init.d/redis-server restart
  //    stop, start
  // watch ts changes - npm run watch
  // run server - npm run dev
  
  const main = async () =&gt; {
    const orm = await MikroORM.init(microConfig); // initialize database
    await orm.getMigrator().up(); // run migrations before anything else
  
    const app = express();
    app.set(&quot;trust proxy&quot;, 1); // trust first proxy
  
    // this comes before applyMiddleware since use session middleware inside apollo
    const RedisStore = connectRedis(session);
    const redisClient = redis.createClient(); // TODO: TypeError: Cannot read properties of undefined (reading 'createClient')
    redisClient.on(&quot;error&quot;, (err) =&gt; console.log(&quot;Redis Client Error&quot;, err));
    await redisClient.connect();
    app.use(
      session({
        name: &quot;qid&quot;,
        // touch - make request to redis to reset the user's session
        //    if user does something, it means they are active and should reset the timer of automatically logging them out
        //    after 24 hours for example
        // disableTouch: true - keep session forever, can change this later to timed sessions
        store: new RedisStore({ client: redisClient, disableTouch: true }), // tell express session using redis
        cookie: {
          maxAge: 1000 * 60 * 60 * 24 * 365 * 10, // 10 years
          httpOnly: true,
          sameSite: &quot;lax&quot;, // csrf
          secure: __prod__, // only works in https
        },
        saveUninitialized: false,
        secret: &quot;askljdhfjkalshdjlf&quot;, // want to keep this secret separately
        resave: true,
        rolling: true,
      })
    );
  
    // app.use(function (req, res, next) {
    //   res.header(
    //     &quot;Access-Control-Allow-Origin&quot;,
    //     &quot;https://studio.apollographql.com&quot;
    //   );
    //   res.header(&quot;Access-Control-Allow-Credentials&quot;, &quot;true&quot;);
    //   next();
    // });
  
    const apolloServer = new ApolloServer({
      schema: await buildSchema({
        resolvers: [HelloResolver, PostResolver, UserResolver],
        validate: false,
      }),
      // object that is accessible by resolvers, basically pass the database itself
      context: ({ req, res }): MyContext =&gt; ({ em: orm.em, req, res }),
      plugins: [
        ApolloServerPluginLandingPageGraphQLPlayground({
          settings: { &quot;request.credentials&quot;: &quot;include&quot; },
        }),
      ],
    });
  
    await apolloServer.start();
    const corsOptions = {
      origin: new RegExp(&quot;/*/&quot;),
      credentials: true,
    };
    apolloServer.applyMiddleware({ app, cors: corsOptions }); // create graphql endpoint on express
    app.listen(4000, () =&gt; {
      console.log(&quot;Server started on localhost:4000&quot;);
    });
  };
  
  main().catch((error) =&gt; {
    console.log(&quot;----------MAIN CATCHED ERROR----------&quot;);
    console.error(error);
    console.log(&quot;-----------------END------------------&quot;);
  });
  </code></pre>
  <p>user.ts</p>
  <pre><code>import {
    Resolver,
    Arg,
    Mutation,
    InputType,
    Field,
    Ctx,
    ObjectType,
  } from &quot;type-graphql&quot;;
  import { User } from &quot;../entities/User&quot;;
  import { MyContext } from &quot;../types&quot;;
  import argon2 from &quot;argon2&quot;;
  
  // another way to implementing arguments for methods instead of @Arg()
  @InputType()
  class UsernamePasswordInput {
    @Field()
    username: string;
    @Field()
    password: string;
  }
  
  @ObjectType()
  class FieldError {
    @Field()
    field: string;
    @Field()
    message: string;
  }
  
  @ObjectType()
  class UserResponse {
    @Field(() =&gt; [FieldError], { nullable: true })
    errors?: FieldError[];
    @Field(() =&gt; User, { nullable: true })
    user?: User;
  }
  
  @Resolver()
  export class UserResolver {
    @Mutation(() =&gt; UserResponse)
    async register(
      @Arg(&quot;options&quot;) options: UsernamePasswordInput,
      @Ctx() { em }: MyContext
    ): Promise&lt;UserResponse&gt; {
      if (options.username.length &lt;= 2) {
        return {
          errors: [
            { field: &quot;username&quot;, message: &quot;length must be greater than 2&quot; },
          ],
        };
      }
      if (options.password.length &lt;= 2) {
        return {
          errors: [
            { field: &quot;password&quot;, message: &quot;length must be greater than 2&quot; },
          ],
        };
      }
      // argon2 is a password hasher package
      const hashedPassword = await argon2.hash(options.password);
      const user = em.create(User, {
        username: options.username,
        password: hashedPassword,
      });
      try {
        await em.persistAndFlush(user);
      } catch (error) {
        // duplicate username error
        if (error.code === &quot;23505&quot;) {
          // || error.detail.includes(&quot;already exists&quot;)
          return {
            errors: [{ field: &quot;username&quot;, message: &quot;Username already taken&quot; }],
          };
        }
      }
  
      // return user in an object since response is now a response object - UserResponse
      return { user };
    }
  
    @Mutation(() =&gt; UserResponse)
    async login(
      @Arg(&quot;options&quot;) options: UsernamePasswordInput,
      @Ctx() { em, req }: MyContext
    ): Promise&lt;UserResponse&gt; {
      // argon2 is a password hasher package
      const user = await em.findOne(User, {
        username: options.username,
      });
      // can give same field error message like invalid login
      if (!user) {
        return {
          errors: [{ field: &quot;username&quot;, message: &quot;That username doesn't exist&quot; }],
        };
      }
      const valid = await argon2.verify(user.password, options.password);
      if (!valid) {
        return {
          errors: [{ field: &quot;password&quot;, message: &quot;Incorrect password&quot; }],
        };
      }
      // mutation {
      //   login(options: {username: &quot;eric&quot;, password: &quot;eric&quot;}) {
      //     errors {
      //       field
      //       message
      //     }
      //     user {
      //       id
      //       username
      //     }
      //   }
      // }
      console.log(req.session)
      console.log(user.id)
      req.session.userId = user.id
      console.log(req.session)
      console.log(req.session.id)
      // console.log(req.session.userId)
  
      // return user in an object since response is now a response object - UserResponse
      return { user };
    }
  }
  </code></pre>
  <p>types.ts</p>
  <pre><code>import { EntityManager, IDatabaseDriver, Connection } from &quot;@mikro-orm/core&quot;;
  import { Request, Response } from &quot;express&quot;;
  import { Session, SessionData } from &quot;express-session&quot;;
  
  // this is the type of orm.em from index.ts
  // extracted to make code look cleaner in post.ts
  export type MyContext = {
    em: EntityManager&lt;any&gt; &amp; EntityManager&lt;IDatabaseDriver&lt;Connection&gt;&gt;;
    req: Request &amp; {
      session: Session &amp; Partial&lt;SessionData&gt; &amp; { userId?: number };
    };
    res: Response;
  };
  </code></pre>