In my last article, I gave a global introduction to GraphQL. I compared it with REST, as the two of them tend to do the same work, but there are some differences in term of quality and performance.
So in this article, we’re going to build a simple movie app, where we can show, add, edit, and delete movies. That way we’ll get through the basics of GraphQL, which is the main goal of this article — as I assume everyone reading this has already worked with NodeJS and MongoDB.
Also Read:- A Simple CRUD App Using GraphQL, NodeJS, and MongoDB
Creating Project and Installing Dependencies
First, you create a new project folder on your local disk. For instance, I have named mine graphql-tuto. In your Node command prompt, enter:
cd graphql-tuto
npm install express mongoose body-parser cors --save
Now we have installed Express, Mongoose, body-parser, and CORS. I’m not going to detail this since it’s not the main goal of this tutorial.
npm install apollo-server-express --save
From the Apollo Docs, I found that the “Apollo Server is a community-maintained open-source GraphQL server that works with all Node.js HTTP server frameworks,” such as Express.
So create a file named app.js
. Add the following code to it to set up the Apollo Express Server and the MongoDB database.
const express = require('express');
const mongoose = require('mongoose');
const schema = require('./schema');
const bodyParser = require('body-parser');
const cors = require('cors');
const { ApolloServer } = require('apollo-server-express');
const url = "mongodb://localhost:27017/moviesdb";
const connect = mongoose.connect(url, { useNewUrlParser: true });
connect.then((db) => {
console.log('Connected correctly to server!');
}, (err) => {
console.log(err);
});
const server = new ApolloServer({
typeDefs: schema.typeDefs,
resolvers: schema.resolvers
});
const app = express();
app.use(bodyParser.json());
app.use('*', cors());
server.applyMiddleware({ app });
app.listen({ port: 4000 }, () =>
console.log(`???? Server ready at http://localhost:4000${server.graphqlPath}`));
GraphQL has two main principles in order to work: types and resolvers. I defined them in Apollo Server. We’ll import them from the file we’ll create later.
Also Read:- Develop RESTful API using Node JS, Express JS
For the time being, let’s create the file models/movie.js
that’ll contain the movie-Mongoose model.
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const movieSchema = new Schema({
name: {
type: String,
required: true
},
rating: {
type: Number,
required: true
},
producer: {
type: String,
required: true
}
}, {
timestamps: true
});
var Movies = mongoose.model('Movie', movieSchema);
module.exports = {Movies, movieSchema};
Every movie will have a name and a producer of type String
and a rating of type Number
.
So now we add a new file, schema.js
, where we’ll build our GraphQL API.
const { gql } = require('apollo-server-express');
const Movie = require('./models/movie').Movies;
First, we import GraphQL as gql
from Apollo Server and our movie-Mongoose model.
GraphQL APIs consist of types and resolvers.
Also Read:- How to Build a Real-time Chat App With NodeJS, Socket.IO, and MongoDB
Types are where we define our model or schema of the entity we want to represent (Movie
in our example). In type, we also declare our queries (getting the list of movies, for example) and mutations (adding a moviem for example). GraphQL types are limited to ID
for IDs, Int
for integer numbers, Float
for float numbers, String
, and Boolean
. So in our schema file, we add these lines of code:
const typeDefs = gql `
type Movie {
id: ID!
name: String!
producer: String!
rating: Float!
}
type Query {
getMovies: [Movie]
getMovie(id: ID!): Movie
}
type Mutation {
addMovie(name: String!, producer: String!, rating: Float!): Movie
updateMovie(name: String!, producer: String!, rating: Float): Movie
deleteMovie(id: ID!): Movie
}
We defined the Movie
type and, of course, each movie will have an id
. We put !
next to types (e.g., String!
) to indicate this property is required. In our example, everything is required. In the Query
type, we have the queries (GET operations).
And in the Mutation
type, we have the other operations that’ll modify or make some changes to our database. In getMovies
, we return a list of movies — that’s why we have the brackets, [Movie]
.
That’s it for the first part of our GraphQL API. Now, we move to link this with Mongoose database queries that’ll directly affect the database. And here’s where the resolvers come in.
const resolvers = {
Query: {
getMovies: (parent, args) => {
return Movie.find({});
},
getMovie: (parent, args) => {
return Movie.findById(args.id);
}
},
Mutation: {
addMovie: (parent, args) => {
let Movie = new Movie({
name: args.name,
producer: args.producer,
rating: args.rating,
});
return Movie.save();
},
updateMovie: (parent, args) => {
if (!args.id) return;
return Movie.findOneAndUpdate(
{
_id: args.id
},
{
$set: {
name: args.name,
producer: args.producer,
rating: args.rating,
}
}, {new: true}, (err, Movie) => {
if (err) {
console.log('Something went wrong when updating the movie');
} else {
}
}
);
}
}
}
The queries and mutations we declared in the type definitions are detailed as operations that interact with the database. I assume you are familiar with Mongoose since it’s not the point of this article, which is mainly written for GraphQL.
Now we’re going to test our API in GraphQL Playground. So in your navigator, go to http://localhost:4000/graphql.