In this blog post, we will explore how to integrate MongoDB as a datasource to an Apollo GraphQL server using TypeScript. We assume you have already set up the required environment variables for MongoDB connection.
Step 1: Install Dependencies
In your project directory, open the terminal and run the following command to install the required packages:
npm install @apollo/server mongodb apollo-mongodb-datasource jsonwebtoken
Configuring MongoDB Connection
The code provided already has the MongoDB connection configuration. We are using the MongoClient
from the mongodb
package to connect to the MongoDB cluster.
import { MongoClient } from "mongodb";
import jwt from "jsonwebtoken";
const clusterUrl = process.env.MONGO_CLUSTER_URL;
const username = encodeURIComponent(process.env.MONGO_USER as string);
const password = encodeURIComponent(process.env.MONGO_PASS as string);
const authMechanism = "DEFAULT";
const mongoUri = `mongodb+srv://${username}:${password}@${clusterUrl}/?authMechanism=${authMechanism}`;
const client = new MongoClient(mongoUri);
client.connect().catch(console.error);
Defining the ApolloContext
Next, let’s define the ApolloContext
interface to include the MongoDB data source we will use in our resolvers:
import { BaseContext } from "@apollo/server";
import jwt from "jsonwebtoken";
export interface ApolloContext extends BaseContext {
authorization?: string;
jwt?: jwt.JwtPayload | null | string;
dataSources: {
listings: Listings;
};
}
Creating the MongoDB Data Source
Now, let’s create a MongoDB data source called Listings
that extends MongoDataSource
from the apollo-mongodb-datasource
package. This data source will interact with the listings
collection in the amazon
database.
import MongoDataSource from "apollo-mongodb-datasource";
export class Listings extends MongoDataSource {
getListings = async (uuid: string, asin: string) =>
await this.find({
"product.asin": asin,
"user.uuid": uuid,
});
}
Integrating MongoDB Data Source
In the code provided, the MongoDB data source has already been integrated into the Apollo server context.
import { ApolloServer } from "@apollo/server";
import { Listings } from "./path/to/Listings"; // Replace with the correct path
const server = new ApolloServer({
resolvers,
typeDefs,
introspection: true,
plugins: [authenticationMiddleware(["LoginMutation", "Login"])],
});
export default startServerAndCreateNextHandler(server, {
context: async (req, res) => ({
req,
res,
authorization: req?.headers?.authorization,
jwt: decode(req?.headers?.authorization),
dataSources: {
listings: new Listings(client.db("amazon").collection("listings")),
},
}),
});
In the context function, we instantiate the Listings
data source and pass the MongoDB database
collection listings
to the data source constructor.
Implementing Resolvers
Finally, let’s integrate the provided resolver code with our Apollo server and connect it to the MongoDB data source.
import { ApolloContext } from "@/pages/api/graphql";
import { ApolloError } from "apollo-server";
import type { Document } from "mongodb";
export async function getListings(
_: unknown,
input: { asin: string },
{ dataSources: { listings }, jwt }: ApolloContext
): Promise<Document[] | ApolloError> {
try {
return await listings?.getListings(
(jwt as { uuid: string })?.uuid,
input.asin
);
} catch (error) {
return new ApolloError("error in getListing resolver");
}
}
export const resolvers = {
Query: {
getListings,
//...
},
//...
};
By adding the getListings
resolver to the Query
object, we’ve integrated our resolver with the Apollo server. The resolver uses the Listings
data source to fetch data from MongoDB based on the input parameters (asin
) and the user’s JWT (jwt
). If an error occurs during the process, an ApolloError
is thrown to handle the error gracefully.
Conclusion
Now, your GraphQL server can interact with MongoDB to provide data to your clients seamlessly.
Happy hacking 🚀