NodeJs、Apollo 和 typescript 服务器挂在会话存储上
NodeJs, Apollo, and typescript server hang on session storage
我是 NodeJs 的新手,我正在学习 Ben Awad 制作的全栈教程 https://www.youtube.com/watch?v=I6ypD7qv3Z8&t=7186s。
设置服务器后一切正常。我为会话存储添加了 express-session,并使用 Redis-connect 和 Redis 客户端将其与 Redis 链接。
这是我的 index.ts:
import { MikroORM } from "@mikro-orm/core";
import { ApolloServer } from "apollo-server-express";
import connectRedis from "connect-redis";
import express from "express";
import session from "express-session";
import { createClient } from "redis";
import "reflect-metadata";
import { buildSchema } from "type-graphql";
import { __prod__ } from "./constants";
import mikroOrmConfig from "./mikro-orm.config";
import { PostResolver } from "./resolvers/post";
import { UserResolver } from "./resolvers/user";
import { MyContext } from "./types";
const main = async () => {
const orm = await MikroORM.init(mikroOrmConfig);
await orm.getMigrator().up();
const app = express();
const RedisStore = connectRedis(session);
const redisClient = createClient();
await redisClient.connect();
await redisClient.ping();
redisClient.on("error", err => console.log("Redis Client Error", err));
app.use(
session({
name: "qid",
store: new RedisStore({ client: redisClient, disableTouch: true }),
secret: "qsfniuiqsdnigu",
saveUninitialized: false,
resave: false,
cookie: {
maxAge: 1000 * 60 * 60 * 24 * 365 * 10,
httpOnly: true,
sameSite: "lax",
secure: __prod__,
},
})
);
const apolloServer = new ApolloServer({
schema: await buildSchema({
resolvers: [UserResolver, PostResolver],
validate: false,
}),
context: ({ req, res }): MyContext => ({ em: orm.em, req, res }),
});
await apolloServer.start();
apolloServer.applyMiddleware({ app });
app.listen(4000, () => {
console.log(`listening on http://localhost:${4000}`);
});
};
main().catch(err => console.error("Errors: ", err));
还有我的 types.ts 文件:
import { Connection, EntityManager, IDatabaseDriver } from "@mikro-orm/core";
import { Request, Response } from "express";
declare module "express-session" {
interface Session {
userId: number;
}
}
export type MyContext = {
em: EntityManager<IDatabaseDriver<Connection>>;
req: Request;
res: Response;
};
export type FieldError = {
errors: [
{
field: string;
message: string;
}
];
};
还有我的 user.ts 解析器,我认为问题出现在登录突变中:
import argon2 from "argon2";
import {
Arg,
Ctx,
Field,
InputType,
Mutation,
ObjectType,
Resolver,
} from "type-graphql";
import { User } from "../entities/User";
import { MyContext } from "../types";
@InputType()
class LoginInputs {
@Field()
username: string;
@Field()
password: string;
}
@ObjectType()
class FieldError {
@Field()
field: string;
@Field()
message: string;
}
@ObjectType()
class UserResponse {
@Field(() => [FieldError], { nullable: true })
errors?: FieldError[];
@Field(() => User, { nullable: true })
user?: User;
}
@Resolver()
export class UserResolver {
@Mutation(() => UserResponse)
async login(
@Arg("options") options: LoginInputs,
@Ctx() { em, req }: MyContext
): Promise<UserResponse> {
const { username, password } = options;
const user = await em.findOne(User, { username });
const errors = {
errors: [
{
field: "username or password",
message: "There is no user with the given credentials",
},
],
};
if (!user) {
return errors;
}
const valid = await argon2.verify(user.password, password);
if (!valid) {
return errors;
}
req.session.userId = user.id; // If I comment this line everything works fine
return { user };
}
}
我正在使用 Node LTS(目前为 16.13.1),这是 package.json:
{
"name": "lirredit-backend",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"watch": "tsc -w",
"dev": "nodemon dist/index.js",
"start": "node dist/index.js",
"create:migration": "mikro-orm migration:create"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/connect-redis": "^0.0.18",
"@types/express": "^4.17.13",
"@types/express-session": "^1.17.4",
"@types/node": "^17.0.14",
"nodemon": "^2.0.15",
"ts-node": "^10.4.0",
"typescript": "^4.5.5"
},
"dependencies": {
"@mikro-orm/cli": "^4.5.10",
"@mikro-orm/core": "^4.5.10",
"@mikro-orm/migrations": "^4.5.10",
"@mikro-orm/postgresql": "^4.5.10",
"apollo-server-core": "^3.6.2",
"apollo-server-express": "^3.6.2",
"argon2": "^0.28.4",
"class-validator": "^0.13.2",
"connect-redis": "^6.0.0",
"cors": "^2.8.5",
"express": "^4.17.2",
"express-session": "^1.17.2",
"graphql": "15.7.2",
"pg": "^8.7.1",
"redis": "^4.0.3",
"reflect-metadata": "^0.1.13",
"type-graphql": "^1.1.1"
},
"mikro-orm": {
"useTsNode": true,
"configPaths": [
"./src/mikro-orm.config.ts",
"./dist/mikro-orm.config.js"
]
}
}
向 localhost:4000/graphql 发出请求并调用登录突变后,请求挂起,如果我尝试刷新页面,客户端无法连接到 localhost:4000/graphql,直到我删除cookie 缓存。
我已经在 Chrome 中尝试过使用两个帐户,Firefox、Edge、Apollo Studio、Thunder Client 和 Postman;并且都产生相同的行为。
值得注意的是,我正在使用 WSL2 和 Ubuntu 发行版,但我将我的项目移至 Windows,但同样的问题仍然存在。
非常感谢任何帮助我是 Node 的新手,在发布之前我已经搜索了这个问题 4 天。谢谢。
redis的问题。对于较新的版本,它以不同的方式保存密钥。
如果你想使用与Ben相同的代码,你必须使用legacyMode:
import { createClient } from 'redis';
const RedisStore = connectRedis(session);
const redisClient = createClient({ legacyMode: true });
await redisClient.connect();
我是 NodeJs 的新手,我正在学习 Ben Awad 制作的全栈教程 https://www.youtube.com/watch?v=I6ypD7qv3Z8&t=7186s。
设置服务器后一切正常。我为会话存储添加了 express-session,并使用 Redis-connect 和 Redis 客户端将其与 Redis 链接。
这是我的 index.ts:
import { MikroORM } from "@mikro-orm/core";
import { ApolloServer } from "apollo-server-express";
import connectRedis from "connect-redis";
import express from "express";
import session from "express-session";
import { createClient } from "redis";
import "reflect-metadata";
import { buildSchema } from "type-graphql";
import { __prod__ } from "./constants";
import mikroOrmConfig from "./mikro-orm.config";
import { PostResolver } from "./resolvers/post";
import { UserResolver } from "./resolvers/user";
import { MyContext } from "./types";
const main = async () => {
const orm = await MikroORM.init(mikroOrmConfig);
await orm.getMigrator().up();
const app = express();
const RedisStore = connectRedis(session);
const redisClient = createClient();
await redisClient.connect();
await redisClient.ping();
redisClient.on("error", err => console.log("Redis Client Error", err));
app.use(
session({
name: "qid",
store: new RedisStore({ client: redisClient, disableTouch: true }),
secret: "qsfniuiqsdnigu",
saveUninitialized: false,
resave: false,
cookie: {
maxAge: 1000 * 60 * 60 * 24 * 365 * 10,
httpOnly: true,
sameSite: "lax",
secure: __prod__,
},
})
);
const apolloServer = new ApolloServer({
schema: await buildSchema({
resolvers: [UserResolver, PostResolver],
validate: false,
}),
context: ({ req, res }): MyContext => ({ em: orm.em, req, res }),
});
await apolloServer.start();
apolloServer.applyMiddleware({ app });
app.listen(4000, () => {
console.log(`listening on http://localhost:${4000}`);
});
};
main().catch(err => console.error("Errors: ", err));
还有我的 types.ts 文件:
import { Connection, EntityManager, IDatabaseDriver } from "@mikro-orm/core";
import { Request, Response } from "express";
declare module "express-session" {
interface Session {
userId: number;
}
}
export type MyContext = {
em: EntityManager<IDatabaseDriver<Connection>>;
req: Request;
res: Response;
};
export type FieldError = {
errors: [
{
field: string;
message: string;
}
];
};
还有我的 user.ts 解析器,我认为问题出现在登录突变中:
import argon2 from "argon2";
import {
Arg,
Ctx,
Field,
InputType,
Mutation,
ObjectType,
Resolver,
} from "type-graphql";
import { User } from "../entities/User";
import { MyContext } from "../types";
@InputType()
class LoginInputs {
@Field()
username: string;
@Field()
password: string;
}
@ObjectType()
class FieldError {
@Field()
field: string;
@Field()
message: string;
}
@ObjectType()
class UserResponse {
@Field(() => [FieldError], { nullable: true })
errors?: FieldError[];
@Field(() => User, { nullable: true })
user?: User;
}
@Resolver()
export class UserResolver {
@Mutation(() => UserResponse)
async login(
@Arg("options") options: LoginInputs,
@Ctx() { em, req }: MyContext
): Promise<UserResponse> {
const { username, password } = options;
const user = await em.findOne(User, { username });
const errors = {
errors: [
{
field: "username or password",
message: "There is no user with the given credentials",
},
],
};
if (!user) {
return errors;
}
const valid = await argon2.verify(user.password, password);
if (!valid) {
return errors;
}
req.session.userId = user.id; // If I comment this line everything works fine
return { user };
}
}
我正在使用 Node LTS(目前为 16.13.1),这是 package.json:
{
"name": "lirredit-backend",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"watch": "tsc -w",
"dev": "nodemon dist/index.js",
"start": "node dist/index.js",
"create:migration": "mikro-orm migration:create"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/connect-redis": "^0.0.18",
"@types/express": "^4.17.13",
"@types/express-session": "^1.17.4",
"@types/node": "^17.0.14",
"nodemon": "^2.0.15",
"ts-node": "^10.4.0",
"typescript": "^4.5.5"
},
"dependencies": {
"@mikro-orm/cli": "^4.5.10",
"@mikro-orm/core": "^4.5.10",
"@mikro-orm/migrations": "^4.5.10",
"@mikro-orm/postgresql": "^4.5.10",
"apollo-server-core": "^3.6.2",
"apollo-server-express": "^3.6.2",
"argon2": "^0.28.4",
"class-validator": "^0.13.2",
"connect-redis": "^6.0.0",
"cors": "^2.8.5",
"express": "^4.17.2",
"express-session": "^1.17.2",
"graphql": "15.7.2",
"pg": "^8.7.1",
"redis": "^4.0.3",
"reflect-metadata": "^0.1.13",
"type-graphql": "^1.1.1"
},
"mikro-orm": {
"useTsNode": true,
"configPaths": [
"./src/mikro-orm.config.ts",
"./dist/mikro-orm.config.js"
]
}
}
向 localhost:4000/graphql 发出请求并调用登录突变后,请求挂起,如果我尝试刷新页面,客户端无法连接到 localhost:4000/graphql,直到我删除cookie 缓存。
我已经在 Chrome 中尝试过使用两个帐户,Firefox、Edge、Apollo Studio、Thunder Client 和 Postman;并且都产生相同的行为。
值得注意的是,我正在使用 WSL2 和 Ubuntu 发行版,但我将我的项目移至 Windows,但同样的问题仍然存在。
非常感谢任何帮助我是 Node 的新手,在发布之前我已经搜索了这个问题 4 天。谢谢。
redis的问题。对于较新的版本,它以不同的方式保存密钥。
如果你想使用与Ben相同的代码,你必须使用legacyMode:
import { createClient } from 'redis';
const RedisStore = connectRedis(session);
const redisClient = createClient({ legacyMode: true });
await redisClient.connect();