Chapter 4: Apollo Server
Strongly Typed Next.js
link Apollo Server
By the end of this lesson, developers will be able to:
- Create a GraphQL API server with Apollo Server Express
- Create a MongoDB database connection with Connect Mongo
- Create environment variables for your localhost server
Introduction
In the previous section, we wrote a strongly typed schema for our GraphQL API server. In this section we will complete the implementation with Apollo Server and Express. Apollo Server Express allows us to build a GraphQL query interface on top of an existing Express server.
Let's begin with an overview of these libraries and their dependencies:
- Apollo Server: An open-source GraphQL server that is compatible with any kind of GraphQL client. We won’t be using Express for our server in this project. Instead, we will use the power of Apollo Server to expose our GraphQL API.
- Express: Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.
- dotenv: We will use dotenv to load environment variables from our .env file.
- nodemon: A tool that helps develop Node-based applications by automatically restarting the node application when changes in the directory are detected. We don’t want to be closing and starting the server every time there’s a change in our code. Nodemon inspects changes every time in our app and automatically restarts the server.
link Installation
Heads Up! This lesson's code edits will only affect your API project.
First, change directory into your root level api
folder:
cd api
Let's begin with the installation of Apollo Server Express:
npm install apollo-server-express cors ts-node
npm install --save-dev nodemon
Server Scripts
Next, we will edit package.json
with two new scripts:
/* ... */
"scripts": {
"start": "ts-node --transpile-only server/index.ts",
"dev": "nodemon server/index.ts"
},
/* ... */
The ts-node --transpile-only
command will skip type checking and compile the server code.
The nodemon
command will run a development server, and reload when any changes occur.
Based on the above scripts, we will also create directories with the following contents:
mkdir session
mkdir server
Each of these directories contains a unique purpose:
api/session
: Mongoose creates the server sessionsapi/server
: Apollo Server Express runs the server
Before moving on to the session
and server
, we will define a new schema
.
link Schema
Create a new file, api/schema/index.ts
and insert the following:
import { GraphQLSchema } from 'graphql';
import { buildSchema } from 'type-graphql';
import { ObjectId } from 'mongodb';
import path from 'path';
import { UserResolver } from '../resolvers/UserResolver';
import { AuthResolver } from '../resolvers/AuthResolver';
import { StreamResolver } from '../resolvers/StreamResolver';
import { ObjectIdScalar } from './object-id.scalar';
import { TypegooseMiddleware } from '../middleware/typegoose';
// build TypeGraphQL executable schema
export default async function createSchema(): Promise<GraphQLSchema> {
const schema = await buildSchema({
// 1. add all typescript resolvers
resolvers: [UserResolver, AuthResolver, StreamResolver],
emitSchemaFile: path.resolve(__dirname, 'schema.gql'),
// 2. use document converting middleware
globalMiddlewares: [TypegooseMiddleware],
// 3. use ObjectId scalar mapping
scalarsMap: [{ type: ObjectId, scalar: ObjectIdScalar }],
validate: false,
});
return schema;
}
In the above code, we do the following:
- Add all typescript resolvers into the schema from the
../resolvers
directory. - Apply typegoose middleware to allow GraphQL work with MongoDB Documents.
- Apply the scalars map to parse
ObjectId
properties into string values.
Up next, we will create a free MongoDB Atlas account and cluster.
link MongoDB Atlas
We recommend creating a free tier database cluster at MongoDB Atlas. Sign up for MongoDB Atlas and follow this simple tutorial to create a free cluster.
MongoDB Atlas is the global cloud database service for modern applications. Deploy fully managed MongoDB across AWS, Azure, or GCP. Best-in-class automation and proven practices guarantee availability, scalability, and compliance with the most demanding data security and privacy standards. Use MongoDB’s robust ecosystem of drivers, integrations, and tools to build faster and spend less time managing your database.
We are going to be using an instance of the MongoDB Atlas database.
- Navigate to the MongoDB Atlas page
- Click "Start Free" and sign up for the MongoDB account
- On the "Projects" page click on "New Project" give it a name and create
- Add Members. You’re already a member -> hit continue
- Build Cluster -> Select Free Tier
- Select Cloud Provider & Region and Create Cluster
- After the cluster was initialized click on "connect"
Choose a connection method -> Select Connect Your Application and select Node.js
Copy and save your connection string, which should look like the following:
mongodb+srv://<dbname>:<password>@cluster0-yvwjx.mongodb.net/<dbname>?retryWrites=true&w=majority
All set! Up next, we will store the connection string as a new environment variable called MONGO_URL
.
link Environment Setup
Before creating the session, we will install dotenv
to initialize the MONGO_URL
environment value.
npm install dotenv
npm install --save-dev @types/dotenv
Create a new file, api/server/env.ts
and insert the following:
import { config } from 'dotenv';
const result = config();
// Only override process.env if .env file is present and valid
if (!result.error) {
const parsed = result.parsed;
if (parsed) {
Object.keys(parsed).forEach((key) => {
const value = parsed[key];
if (value) {
process.env[key] = value;
console.log(`${key}=${value}`);
}
});
}
}
Using the dotenv
library, we are able to determine if a valid .env
file is present and log all it's respective values.
Environment File
Create a new file, api/.env
and insert the following:
MONGO_URL=REPLACE_WITH_MONGO_URL
URL_APP=http://localhost:3000
Be sure to include your connection url (MONGO_URL
) from MongoDB Atlas.
Remember, this file is not meant to be committed with your git repository, as it contains sensitive information.
Up next, we will implement the mongo session.
link Session
Create a new file, api/session/index.ts
and insert the following:
import { connect } from 'mongoose';
export default async function createSession() {
const MONGO_URL = process.env.MONGO_URL || '';
if (!MONGO_URL) {
throw new Error('Missing MONGO_URL');
}
const options = {
useNewUrlParser: true,
useCreateIndex: true,
useFindAndModify: false,
useUnifiedTopology: true,
};
await connect(MONGO_URL, options);
}
In the above code, we initialize a MongoDB connection. If any errors are thrown, they will be handled by the server at runtime.
link Server
Create a new file, api/server/index.ts
and insert the following:
import './env';
import 'reflect-metadata';
import { ApolloServer } from 'apollo-server-express';
import express from 'express';
import cors from 'cors';
import createSchema from '../schema';
import createSession from '../session';
const port = process.env.PORT || 8000;
async function createServer() {
try {
// 1. create mongoose connection
await createSession();
// 2. create express server
const app = express();
// allow CORS from client app
const corsOptions = {
origin: 'http://localhost:3000',
credentials: true,
};
app.use(cors(corsOptions));
// allow JSON requests
app.use(express.json());
const schema = await createSchema();
// 3. create GraphQL server
const apolloServer = new ApolloServer({
schema,
context: ({ req, res }) => ({ req, res }),
introspection: true,
// enable GraphQL Playground with credentials
playground: {
settings: {
'request.credentials': 'include',
},
},
});
apolloServer.applyMiddleware({ app, cors: corsOptions });
// start the server
app.listen({ port }, () => {
console.log(
`🚀 Server ready at http://localhost:${port}${apolloServer.graphqlPath}`
);
});
} catch (err) {
console.log(err);
}
}
createServer();
The ApolloServer
is initialized with an Express server, MongoDB session middleware and GraphQL schema. Most of the logic is abstracted out to simplify this file, so here's an overview:
- We initialize a MongoDB database connection.
- We initialize an Express server and apply
cors
,json
andsession
middleware. - We initialize an Apollo GraphQL server with the schema definition, context and playground options.
Note: Setting
'request.credentials': 'include',
in the playground options is necessary for testing authenticated requests.
link Testing
It's time to fire up the server and test our queries.
npm run dev
Be sure to visit the playground and test your queries at http://localhost:8000/graphql
Congratulations!
Today we completed the GraphQL API server. Up next, we will begin to integrate an Apollo Client with the frontend.