In this article, I'm going to show how to create a GraphQL Server using ExpressJS and Apollo Server.
Crete the project folder and install Apollo Server and Express
mkdir graphql-server-playground
cd graphql-server-playground
npm init -y
The package.json
file should be something like this
{
"name": "graphql-server-playground",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
Add description, keywords and author if you want.
In order to setup Express and Apollo Server, install the following dev dependencies
npm install --save-dev @babel/cli @babel/core @babel/node @babel/preset-env nodemon
and the following dependencies
- apollo-server-express
- express
- graphql
- graphql-import
- graphql-tools
npm install apollo-server-express express graphql graphql-import graphql-tools
Now, in the root folder of your project, create the src
folder and the index.js
file in it
import express from 'express';
import { ApolloServer, gql } from 'apollo-server-express';
const typeDefs = gql`
type Query {
hello: String
}
`;
const resolvers = {
Query: {
hello: () => 'Hello world!',
},
};
const server = new ApolloServer({ typeDefs, resolvers });
const app = express();
server.applyMiddleware({ app });
app.listen({ port: 4001 }, () =>
console.log(`🚀 Server ready at http://localhost:4001${server.graphqlPath}`)
);
Update the package.json
file
{
...
"main": "src/index.js",
"scripts": {
"start": "nodemon --exec babel-node -- ./src/index.js"
},
...
}
Nodemon
is a tool that helps develop node.js based applications by automatically restarting the node application when file changes in the directory are detected.
Before running the server, we have to create a .babelrc
file in the root folder of the project
{
"presets": ["@babel/preset-env"]
}
This file tells NodeJS we are using Babel 7 and that the code has to be transpiled for the browser.
Now start the server with npm start
npm start
> graphql-server-playground@1.0.0 start graphql-server-playground
> nodemon --exec babel-node -- ./src/index.js
[nodemon] 2.0.7
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `babel-node ./src/index.js`
🚀 Server ready at http://localhost:4001/graphql
The GraphQL server is now running on your machine at http://localhost:4001/graphql
The Playground
The Playground is where you can run your queries and check for the documentation or your schema.
The only query available now is the following
query {
hello
}
as you can see in the code, it corresponds to this
const typeDefs = gql`
type Query {
hello: String
}
`;
Each query has to have a resolver
. The resolver is a function that defines what the query returns
const resolvers = {
Query: {
hello: () => 'Hello world!',
},
};
In this case, the resolver for the hello
query returns a string, Hello world!
If you want to learn more about GraphQL queries and resolvers, take a look here.
Defining the schema
Before defining the schema, we have to decide what kind of data this server will provide to the client.
Let's say we want to store and serve information about our address book. So the data I exptect to have is a list of people whith names, phone numbers and email addresses. Since we don't have a real database for now, let's mock it in the index.js
file
...
const persons = [
{
id: 1,
name: "Ash Ketchum",
phone_number: "+440000000000",
email: "ash@gmail.com",
},
{
id: 2,
name: "Professor Oak",
phone_number: "+441111111111",
email: "proak@gmail.com",
},
{
id: 3,
name: "Gary Oak",
phone_number: "+442222222222",
},
];
...
This will be our temporary database.
Now the schema:
const typeDefs = gql`
type Person {
id: Int!
name: String!
email: String
}
type Query {
person(id: Int!): Person
}
`;
Let's have a closer look.
Since we already defined the structure of the data, we are pretty sure that what we will receive from the database will have this shape:
- id: Int! (an integer, non nullable)
- name: String! (a string, non nullable)
- email: String (a string, nullable)
The query person(id: Int!): Person
wants an id as parameter (note the id is non nullable - it can't be null or we won't be able to retrieve anything!) and it returns a Person
(an object defined by the Person type). Since it may happen the person we search for is not present in the database, the return type of this query is nullable (it can returns null
).
Let's run the query in the Playground
query {
person(id: 1) {
id
name
email
}
}
Writing a new query
const typeDefs = gql`
...
type Query {
...
persons: [Person]!
}
`;
const resolvers = {
Query: {
...
persons: (_, __, ___) => persons,
},
};
Pretty straight-forward, right? The new query persons
returns the whole persons
array. Let's run the new query on the Playground
Writing a mutation
A Mutation
is a function used to create, update and delete data in GraphQL. If you think at CRUD, mutations represent the C(reate) U(update) D(elete).
Let's create a new type PersonInput
and another root type Mutation
const typeDefs = gql`
...
input PersonInput {
id: Int!
name: String!
email: String
}
type Mutation {
createPerson(person: PersonInput!): Person
}
`;
The function createPerson
accepts a non nullable object of type PersonInput
and returns another object of type Person
. It's just a case the two types Person
and PersonInput
are the same.
The mutation then is something like
const resolvers = {
...
Mutation: {
createPerson(_, { person }, __) => {
persons.push(person);
return person;
},
},
};
For simplicity's sake we are entering manually the ID paying attention to not reuse the same ID twice (or the query person
will unexpectedly return two results!).
Let's use the mutation on the Playground
mutation {
createPerson(person: {id: 5, name: "Brock", email: "brock@gmail.com"}) {
id
name
email
}
}
We can also run the persons
query to see how the list of people is updated
Please remember that we are using an object as temporary database and that you will lose the data you add/change/remove via mutations once you restart the server! If you want some persistency, think about connecting the GraphQL server to a data source. I won't do that in this article.
Improving the code style with Prettier
Install Prettier with
npm install prettier --save-dev
Update your package.json
file as follows
"scripts": {
...
"prettier": "prettier --write ."
},
Create a file .prettierrc.json
in the root folder of your project
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": false,
"singleQuote": true
}
This file contains the configuration of Prettier and the rules it will apply to your code.
Create a second file, .prettierignore
and copy-paste the content of your .gitignore
. This file will simply tell Prettier which files to ignore while formatting the code.
Now when you run npm run prettier
it will apply the formatting rules to all files in the root directory and sub-directories. You can change this replacing
"prettier": "prettier --write ."
with
"prettier": "prettier --write ./src/**"
In this case Prettier will format files in the src
folder and in its sub-folders.
It's a wrap
That's it, you made it! Now you have a GraphQL server running locally and a very useful Playground to execute queries! 🚀
Here's the repository with all the code we wrote in this article https://github.com/rossanodan/graphql-server-playground. Feel free to fork it and use it as your own or as a template.
Top comments (0)