Type-graphql, Mikororm Query returns Ref<User> 而不是 User

Type-graphql, Mikororm Query returns Ref<User> rather than User

我正在使用 Mikro-ORM、Type-graphql、NodeJs、Express、Redis 和 PostgreSQL 创建 Reddit 的克隆版本的项目后端

这是我使用 Mikororm 的初始项目设置,将其连接到 Postgres,并启动 Redis 进行缓存

const orm = await MikroORM.init(microConfig);

  // run the Migration before do anything else
  await orm.getMigrator().up();

  // create an express server app
  const app = express();

  // set up redis running
  const RedisStore = connectRedis(session);
  const redis = new Redis();
  app.use(
    cors({
      origin: "http://localhost:3000",
      credentials: true,
    })
  );
  app.use(
    session({
      name: COOKIE_Name,
      store: new RedisStore({
        client: redis,
        disableTTL: true,
      }),
      cookie: {
        maxAge: 1000 * 60 * 60 * 24 * 365 * 3, // 3 years cookies
        httpOnly: true,
        secure: __prod__, // cookie only works in https
        sameSite: "lax", //csrf: google this for more information
      },
      saveUninitialized: false,
      secret: "nkadniandiuanidnaidhaiddwq",
      resave: false,
    })
  );

  app.get("/", (_, res) => {
    res.send("hello");
  });
  app.listen(4000, () => {
    console.log("server started on localhost:4000");
  });

  // Apollo server
  const apolloServer = new ApolloServer({
    schema: await buildSchema({
      resolvers: [HelloResolver, PostResolver, UserResolver],
      validate: false,
    }),
    context: ({ req, res }): MyContext => ({ em: orm.em, req, res, redis }),
  });

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

我的实体为 Post.js

设置
@ObjectType()
@Entity()
export class Post {
  @Field()
  @PrimaryKey()
  _id!: number;

  @Field()
  @Property()
  creatorId!: number;

  @Field()
  @Property({ type: "date" })
  createdAt: Date = new Date();

  @Field()
  @Property({ type: "date", onUpdate: () => new Date() })
  updatedAt: Date = new Date();

  @Field()
  @Property({ type: "text" })
  title!: string;

  @Field()
  @Property({ type: "text" })
  text!: string;

  @Field()
  @Property({ type: "number" })
  points!: number;

  @Field()
  @ManyToOne(() => User)
  creator: User;

  constructor(creator: User) {
    this.creator = creator;
  }

  @OneToMany(() => Upvote, (upvote) => upvote.post)
  upvote = new Collection<Upvote>(this);
}

和User.js

@ObjectType()
@Entity()
export class User {
  @Field()
  @PrimaryKey()
  @Property()
  _id!: number;

  @Field()
  @Property({ type: "date" })
  createdAt: Date = new Date();

  @Field()
  @Property({ type: "date", onUpdate: () => new Date() })
  updatedAt: Date = new Date();

  @Field()
  @Property({ type: "text", unique: true })
  username!: string;

  @Field()
  @Property({ type: "text", unique: true })
  email!: string;

  @Property({ type: "text" })
  password!: string;

  @OneToMany(() => Post, (post) => post.creator)
  posts = new Collection<Post>(this);

  @OneToMany(() => Upvote, (upvote) => upvote.user)
  upvote = new Collection<Upvote>(this);
}

这是我查询所有帖子的解析器,每个 post 都会有一个创建者(即用户),一个用户可以创建多个 post:

@Query(() => PaginatedPosts)
  async posts(
    @Arg("limit", () => Int) limit: number,
    @Arg("cursor", () => String, { nullable: true }) cursor: string,
    @Ctx() { em }: MyContext
  ) {
    const realLimit = Math.min(50, limit);
    const realLimitPlusOne = realLimit + 1;

    const withCursor = await em
      .getRepository(Post)
      .find(
        { $and: [{ "createdAt <=": cursor }] },
        { limit: realLimitPlusOne, orderBy: { createdAt: "DESC" } }
      );
    const withoutCursor = await em
      .getRepository(Post)
      .find({}, { limit: realLimitPlusOne, orderBy: { createdAt: "DESC" } });

    // console.log("Post with Cursor:", withCursor);

    console.log("Post without Cursor", withoutCursor);

    if (cursor) {
      return {
        posts: withCursor.slice(0, realLimit),
        hasMore: withCursor.length === realLimitPlusOne,
      };
    } else {
      return {
        posts: withoutCursor.slice(0, realLimit),
        hasMore: withoutCursor.length === realLimitPlusOne,
      };
    }
  }

问题出现的时候是这样的,有时Datareturn对我来说只是Ref而不是我想要的User对象

Post {
    _id: 214,
    creatorId: 18,
    createdAt: 2021-07-31T09:48:50.000Z,
    updatedAt: 2021-07-31T09:48:50.000Z,
    title: 'Yay It works ',
    text: ':>>>',
    points: 3,
    creator: Ref<User> { _id: 18 },
    upvote: Collection { initialized: false, dirty: false }
  }

但有时它确实有效,但很多时候查询失败,因为它只是 returnRef<> 而不是整个用户对象

Post {
    _id: 214,
    creatorId: 18,
    createdAt: 2021-07-31T09:48:50.000Z,
    updatedAt: 2021-07-31T09:48:50.000Z,
    title: 'Yay It works ',
    text: ':>>>',
    points: 3,
    creator: User {
      _id: 18,
      createdAt: 2021-07-19T15:30:31.000Z,
      updatedAt: 2021-07-21T06:55:03.000Z,
      username: 'minhquan0902',
      email: 'minhquan0902@gmail.com',
      password: '$argon2i$v=19$m=4096,t=3,p=1$powvhc+mI4O/jmhXz807lg$oZtyE2HRW6Ei6JqPGcEDBdmEnv3D81C9lMNd5kmEKPA',
      posts: [Collection],
      upvote: [Collection]
    },

看起来您没有在任何地方处理请求上下文,因此对所有请求重复使用相同的 EM。每个请求都需要有自己的 EM 分叉。

请参阅 https://mikro-orm.io/docs/identity-map 了解原因。

我没有使用 apollo 的经验,但我相信更改此行应该足够了:

context: ({ req, res }): MyContext => ({ em: orm.em, req, res, redis }),

使用新的 fork 而不是全局 orm.em

context: ({ req, res }): MyContext => ({ em: orm.em.fork(), req, res, redis }),

这是一个以这种方式执行的示例应用程序: https://github.com/driescroons/mikro-orm-graphql-example/blob/master/src/application.ts#L63