Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Express.Session isn't obvious #13

Open
dzirg44 opened this issue Nov 10, 2020 · 29 comments
Open

Express.Session isn't obvious #13

dzirg44 opened this issue Nov 10, 2020 · 29 comments

Comments

@dzirg44
Copy link

dzirg44 commented Nov 10, 2020

Hi @benawad ! Thank you for your great videos.
Today I faced with one problem here

req: Request & { session: Express.Session };

as I can see this type we get from express-session even though you tried to import it from express and it didn't work.
when I try to write something like Express.Session I get the error

Namespace 'global.Express' has no exported member 'Session'

i tried to solve it via, but I am not good at typescript ;)

import { Connection, EntityManager, IDatabaseDriver } from "@mikro-orm/core";
import { Request, Response } from "express";

export type MyContext = {
  em: EntityManager<IDatabaseDriver<Connection>>;
  req: Request & { session: Request };
  res: Response;
};

in server/src/types.ts
and

req.session.sessionID = user.id.toString();

in the server/src/resolvers/user.ts

versions:

{
    "express": "^4.17.1",
    "express-session": "^1.17.1",
}

sorry in advance if I just didn't understand or miss something .

@benawad
Copy link
Owner

benawad commented Nov 10, 2020

Looks like the session type has been removed from express, you could probably just do:

Request & { session: { userId: string } };

@c2peng
Copy link

c2peng commented Nov 11, 2020

Thanks so much for asking this. Spent 6+ hrs trying to figure out what's happening.

@girishk21
Copy link

girishk21 commented Nov 15, 2020

I found this to be working!

in /server/index.d.ts

import { User } from './src/entities/User';

declare global {
  namespace Express {
    interface Session {
      userid?: number;
    }
  }
}

and add this to "files": [] attribute of tsconfig.json.

Express.Session is now available in /server/src/types.ts 🎉

is it okay to play with declaration merging or it's not advisable to do so?

@RiahiKarim
Copy link

@dzirg44 This should work.

import { Request, Response } from 'express';
import { Session, SessionData } from 'express-session';

export type MyContext = {
  req: Request & {
    session: Session & Partial<SessionData> & { userId?: number };
  };
  res: Response;
};

@Sparklytical
Copy link

I did something like this -

interface SessionData {
  userId: number;
}

export type Context = {
  req: Request & { session: SessionData };
};

@uV3301
Copy link

uV3301 commented Nov 22, 2020

@dzirg44 This should work.

import { Request, Response } from 'express';
import { Session, SessionData } from 'express-session';

export type MyContext = {
  req: Request & {
    session: Session & Partial<SessionData> & { userId?: number };
  };
  res: Response;
};

This worked fine. Thanks @dzirg44

@MananDesai54
Copy link

@dzirg44 This should work.

import { Request, Response } from 'express';
import { Session, SessionData } from 'express-session';

export type MyContext = {
  req: Request & {
    session: Session & Partial<SessionData> & { userId?: number };
  };
  res: Response;
};

Thank you, it worked

@MananDesai54
Copy link

Looks like the session type has been removed from express, you could probably just do:

Request & { session: { userId: string } };

Your videos are on the next level, thank you so much for all of this

@CarlosBalladares
Copy link

This solves too

import session from "express-session";
...
Request & { session: session.Session & { userId?: number } };

@ahmedrowaihi
Copy link

import session from "express-session";
declare module "express-session" {
interface SessionData {
userId?: number;
}
}

export type MyContext = {
req: Request & { session: session.Session & Partial<session.SessionData> };
res: Response;
redis: Redis;
usersLoader: ReturnType;
updootLoader: ReturnType;
};

@BookmDan
Copy link

BookmDan commented Jan 2, 2021

I have tried several methods and for whatever reason, there is not a cookie being saved...

@benawad
Copy link
Owner

benawad commented Jan 2, 2021

@BookmDan this is all my advice for debugging cookies: https://benawad.com/cookie

@BookmDan
Copy link

BookmDan commented Jan 2, 2021

Thank you @benawad That was helpful. Actually, because we are incorporating with RedisStore would that be considered a 3rd Party Cookie? Asking for a friend.

@BookmDan
Copy link

BookmDan commented Jan 2, 2021

This is the error message that I am seeing:

Screen Shot 2021-01-02 at 8 15 50 AM (2)

@Tenezill
Copy link

Tenezill commented Jan 4, 2021

I have tried several methods and for whatever reason, there is not a cookie being saved...

hey, the thing that solved the problem for me was "removing" secure while in development.
like this:

app.use(
    session({
      name: "qid",
      store: new RedisStore({
        client: redisClient,
        disableTouch: true,
      }),
      cookie: {
        maxAge: 1000 * 60 * 60 * 24 * 365 * 1, // 10 years
        httpOnly: true,
        sameSite: "lax", // csrf
        // secure: __prod__, // cookie only works in https
      },
      saveUninitialized: false,
      secret: "qowihho",
      resave: false,
    })
  );

@kyawthura-gg
Copy link

I have tried several methods and for whatever reason, there is not a cookie being saved...

I have solved this by setting cors in apollo server.

apolloServer.applyMiddleware({
app,
cors: {
credentials: true,
},
});

@matthew-d-stringer
Copy link

matthew-d-stringer commented Feb 11, 2021

According to express-session, this is what I did:

import { Request } from "express";
import session from "express-session";

declare module "express-session" {
  interface SessionData {
    userId?: number;
  }
}

export type MyContext = {
  req: Request & { session: session.SessionData };
  res: Response;
};

You can see where I got this idea in the Type definition for session.SessionData (ctrl+right click in VsCode):

    /**
     * This interface allows you to declare additional properties on your session object using [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html).
     *
     * @example
     * declare module 'express-session' {
     *     interface SessionData {
     *         views: number;
     *     }
     * }
     *
     */
    interface SessionData {
        cookie: Cookie;
    }

@ahmedrowaihi
Copy link

I guess this issue has to be closed! like all solutions here do work fine!

@BookmDan
Copy link

BookmDan commented Feb 23, 2021 via email

@SSouper
Copy link

SSouper commented Feb 23, 2021

Yes I'm having a similar issue. On to the next project for now...

It seems like the apollo middleware removes every field from req that "should not be there" (session included). I put a middleware function before apollo but after express-session and req.session was there.

@benawad
Copy link
Owner

benawad commented Feb 23, 2021

do you get the same result if you clone my code?

@SSouper
Copy link

SSouper commented Feb 23, 2021

@benawad looks like i forgot to destructure the first argument to the context function. Strangely, typescript didn't tell me that i'm a bad boy...

@AaronMcCloskey
Copy link

AaronMcCloskey commented Apr 3, 2021

@dzirg44 This should work.

import { Request, Response } from 'express';
import { Session, SessionData } from 'express-session';

export type MyContext = {
  req: Request & {
    session: Session & Partial<SessionData> & { userId?: number };
  };
  res: Response;
};

Trying this method has resulted in this error for me

node_modules/@types/connect-redis/index.d.ts:32:72 - error TS2694: Namespace 'global.Express' has no exported member 'SessionData'.

    ttl?: number | string | ((store: RedisStore, sess: Express.SessionData, sid: string) => number);
    

Package.json

devDependencies": {
    "@types/connect-redis": "^0.0.14",
    "@types/express": "^4.17.7",
    "@types/express-session": "^1.17.0",
    "@types/node": "^14.0.27",
    "@types/redis": "^2.8.25",
    "nodemon": "^2.0.4",
    "ts-node": "^8.10.2",
    "typescript": "^3.9.7"
  },
  "dependencies": {
    "@mikro-orm/cli": "^4.0.0-alpha.0",
    "@mikro-orm/core": "^4.0.0-alpha.0",
    "@mikro-orm/migrations": "^4.0.0-alpha.0",
    "@mikro-orm/postgresql": "^4.0.0-alpha.0",
    "apollo-server-express": "^2.16.1",
    "argon2": "^0.26.2",
    "class-validator": "^0.13.1",
    "connect-redis": "^5.0.0",
    "express": "^4.17.1",
    "express-session": "^1.17.1",
    "graphql": "^15.3.0",
    "pg": "^8.3.0",
    "redis": "^3.0.2",
    "reflect-metadata": "^0.1.13",
    "type-graphql": "^1.0.0-rc.3"
  },

The solution I found was adding this, but it feels kind of hacky, is there a better solution?

declare global {
  namespace Express {
    interface SessionData {
      cookie: any;
    }
    interface Session {
      userId?: number;
    }
  }
}

@mattrgibson
Copy link

Don't forget to ensure that the GraphQL playground settings have credentials to include. This took me an hour to remember that Ben mentioned this very early on in the video:

image

@sandeep-v1404
Copy link

Looks like the session type has been removed from express, you could probably just do:

Request & { session: { userId: string } };

It should be

Request & { session: { userId?: number } }

because again in UserResolver me( ) Query, Id is a number.

const user = em.findOne(User, { id: req.session.userId }); // where id is a number.

@skele2k
Copy link

skele2k commented Jul 26, 2021

I have tried several methods and for whatever reason, there is not a cookie being saved...

maybe you should check your constant
it must be
export const __prod__ = process.env.NODE_ENV === "production";
not
export const __prod__ = process.env.NODE_ENV !== "production";

@Deveshb15
Copy link

Deveshb15 commented Dec 5, 2021

Don't forget to ensure that the GraphQL playground settings have credentials to include. This took me an hour to remember that Ben mentioned this very early on in the video:

image

When I change it from "omit" to "include" I get this error, any solution for this?

image

and I get this cors error in the console
image

@gierle
Copy link

gierle commented Apr 14, 2022

I found a weird behaviour and can't really explain why this is:

When setting the req.session.userId to the uid in login or register, the mutation gets executed properly, but the frontend never revieves a response. But when i don't set the 'userID', a response is recieved.

So for example the following code

req.session.userId = user._id; 
console.log(req.session) // this gets printed 
return { user };

would print the session object with cookie and userId, but the frontend never leaves the fetching state and eventually times out.
The same for the playground btw


The solution is an incompatibility between connect-redis and node-redis as described in this issue:
tj/connect-redis#336

Replace this line in index.ts
const redisClient = createClient({ legacyMode: true });

@amarkmcconn
Copy link

This post helps fix the the switch from playground to sandbox if you are unable to test the cookie login

https://stackoverflow.com/questions/69333408/express-session-does-not-set-cookie/70673115#70673115

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests