apollo-datasource-mongodb
apollo-datasource-mongodb copied to clipboard
this.findOneById is not a function (Apollo Server Express v4 GraphQL)
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?
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.
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));
}
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 :)
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. :)
@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: https://github.com/apollographql/apollo-server/discussions/7096
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:
- release a
0.5.5
with peer dep of apollo-server < 4 - release a
0.6.0
with a dep of@apollo/server
instead ofapollo-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 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.
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.
This issue report is going on a year now, any hope on a resolution?