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

this.findOneById is not a function (Apollo Server Express v4 GraphQL) #114

Open
mishanianod opened this issue Oct 15, 2022 · 9 comments
Open

Comments

@mishanianod
Copy link

mishanianod commented Oct 15, 2022

I have the following ApolloServer (v4)

import { MongoDataSource } from 'apollo-datasource-mongodb'

export default class LoaderAsset extends MongoDataSource {
 async getAsset(assetId) {
    return this.findOneById(assetId) // ERROR IS HERE
  }
}

async function startApolloServer(app) {
  const httpServer = http.createServer(app);
  
  const server = new ApolloServer({
    typeDefs,
    resolvers,
    plugins: [ApolloServerPluginDrainHttpServer({ httpServer })]
    
  });

  await server.start();
  app.use(
    '/graphql',
    cors(),
    bodyParser.json(),
    expressMiddleware(server, {
      context: async ({ req }) => {
       return {
        dataSources: {
          loaderAsset: new LoaderAsset(modelAsset),
        }
      }
      },
    }),
  );


  const port = config.get('Port') || 8081;
  await new Promise(resolve => httpServer.listen({ port }, resolve));
}

when I run graphql and send one assetId, everything is working till I get following error:

this.findOneById is not a function

By the way (this.) has collection and model objects but not any methods.

is it because apollo-datasource-mongodb is not compatible with the new version of apollo server v4?

@mishanianod
Copy link
Author

dataSources in v3 were as follows:

 dataSources: () => ({
    users: new Users(client.db().collection('users'))
    // OR
    // users: new Users(UserModel)
  })

but in the new version dataSources is inside the context
Maybe the issue is because of this change.

@mishanianod mishanianod changed the title Apollo Server Express v4 GraphQL this.findOneById is not a function this.findOneById is not a function (Apollo Server Express v4 GraphQL) Oct 15, 2022
@systemkrash
Copy link

systemkrash commented Oct 20, 2022

what i did is i override my datasource class so I can call the initialize method inside in the MongoDatasource class. Now it works for me.

import { MongoDataSource } from 'apollo-datasource-mongodb';

class ReceptionDataSource extends MongoDataSource {
  constructor({ collection, cache }) {
    super(collection);
    super.initialize({ context: this.context, cache });
  }

  async getReception(receptionId) {
    return await this.findOneById(receptionId);
  }
}

export default ReceptionDataSource;

then in my context

async function startApolloServer(app) {
  const httpServer = http.createServer(app);
  
  const server = new ApolloServer({
    typeDefs,
    resolvers,
    plugins: [ApolloServerPluginDrainHttpServer({ httpServer })]
    
  });

  await server.start();
  app.use(
    '/graphql',
    cors(),
    bodyParser.json(),
    expressMiddleware(server, {
      context: async ({ req }) => {
      const { cache } = server
       return {
        dataSources: {
          loaderAsset: new ReceptionDataSource({collection: ReceptionModel, cache}),
        }
      }
      },
    }),
  );


  const port = config.get('Port') || 8081;
  await new Promise(resolve => httpServer.listen({ port }, resolve));
}

@mishanianod
Copy link
Author

what i did is i override my datasource class so I can call the initialize method inside in the MongoDatasource class. Now it works for me.

import { MongoDataSource } from 'apollo-datasource-mongodb';

class ReceptionDataSource extends MongoDataSource {
  constructor({ collection, cache }) {
    super(collection);
    super.initialize({ context: this.context, cache });
  }

  async getReception(receptionId) {
    return await this.findOneById(receptionId);
  }
}

export default ReceptionDataSource;

then in my context

async function startApolloServer(app) {
  const httpServer = http.createServer(app);
  
  const server = new ApolloServer({
    typeDefs,
    resolvers,
    plugins: [ApolloServerPluginDrainHttpServer({ httpServer })]
    
  });

  await server.start();
  app.use(
    '/graphql',
    cors(),
    bodyParser.json(),
    expressMiddleware(server, {
      context: async ({ req }) => {
      const { cache } = server
       return {
        dataSources: {
          loaderAsset: new ReceptionDataSource({collection: ReceptionModel, cache}),
        }
      }
      },
    }),
  );


  const port = config.get('Port') || 8081;
  await new Promise(resolve => httpServer.listen({ port }, resolve));
}

Thanks, @systemkrash , it is working now. hopefully, they update apollo-datasource-mongodb soon :)

@systemkrash
Copy link

i can modify the code but i don't have time to do so because of my tight deadlines. but I think this is the fix. the cache attribute of the class needs to be expose in the constructor to work on apollo-server v4 :D

glad it works for you. :)

@RemyMachado
Copy link

RemyMachado commented Oct 28, 2022

@systemkrash Thank you for your code snippet, it's of great help!
I'm still struggling with a point: How do you correctly set the context in the DataSource (with additional fields)?

super.initialize({ context: this.context, cache });

this.context is always undefined, isn't it?


Also I'll have a problem, even with a defined context. I was exploiting the fact that the dataSources context was containing the other dataSources and himself:

// inside any of my DataSource extended class

this.context.dataSources.users.findOneById(this.context.token)

EDIT:
I got help from a member of ApolloGraphQL and found a way to set correctly the context and dataSources:
apollographql/apollo-server#7096

@lorensr
Copy link
Member

lorensr commented Oct 28, 2022

is it because apollo-datasource-mongodb is not compatible with the new version of apollo server v4?

Sounds like it's not! In which case I think we should:

  1. release a 0.5.5 with peer dep of apollo-server < 4
  2. release a 0.6.0 with a dep of @apollo/server instead of apollo-server-errors, and a fix to this issue.

I'd welcome a PR for # 2.

https://www.apollographql.com/docs/apollo-server/migration/#datasources

@systemkrash
Copy link

@systemkrash Thank you for your code snippet, it's of great help! I'm still struggling with a point: How do you correctly set the context in the DataSource (with additional fields)?

super.initialize({ context: this.context, cache });

this.context is always undefined, isn't it?

Also I'll have a problem, even with a defined context. I was exploiting the fact that the dataSources context was containing the other dataSources and himself:

// inside any of my DataSource extended class

this.context.dataSources.users.findOneById(this.context.token)

EDIT: I got help from a member of ApolloGraphQL and found a way to set correctly the context and dataSources: apollographql/apollo-server#7096

I believed its not undefined because that attribute belongs to MongoDataSource class which we use to extend our own Datasource class. sorry for the late response.

@nnoce14
Copy link
Contributor

nnoce14 commented May 9, 2023

Hi @lorensr , I have been working on upgrading Apollo Server 3 to 4 in my own project where I using this package. I created a fork to update MongoDataSource to work with the changes made to data sources in Apollo Server 4. I believe I have come up with a general implementation that would solve the problem described in this issue as well as resolving issues #115 and #108.

The major change was removing DataSource class and apollo-datasource package, which have been deprecated as Apollo Server v4. In v4, data source classes can either be custom made or extend a community maintained package like this one. That package constructor should have a typed object which contains only what that data source implementation requires.

In this case, I defined a MongoDataSourceConfig interface for the constructor which requires a modelOrCollection and optionally a cache. If you need access to any data in your subclass, you can add them to the argument of the constructor in the subclass and call super passing in the options argument. This way, the user has total control over what data or context is accessible in each data source.

Also, since everything is initialized in the data source constructors and the abstract DataSource class was removed, the initialize method on MongoDataSource is no longer needed. I also removed the Context type argument on MongoDataSource because context, if it's needed, is stored on the subclasses, not MongoDataSource itself.

I also removed some deprecated packages like apollo-server-errors, as in v4, ApolloError was replaced by GraphQLError from the graphql package.

I created a pull request for my fork that you can review. If you have any further questions about the changes I made, feel free to message me. I also updated the README to reflect the changes and some links to relevant documentation on Apollo Server.

@NickDuBois
Copy link

This issue report is going on a year now, any hope on a resolution?

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

6 participants