This site runs best with JavaScript enabled.

CRUD App Using GraphQL Apollo Server, MongoDB And Angular

Software Engineer, React Training, Testing JavaScript Training

Until GraphQL came out, RESTful API was the most popular choice for client-server communication. REST has been defined about 20 years ago, in context of problems from that time. As applications became more complex and new devices came around, new problems has emerged and REST wasn't able to solve them in a performant way. One common scenario is: we want to display on homepage a list of articles and for each article we want to have it's author name and avatar or email. With REST api we need to make few requests to achieve this: one to get the list of articles with author IDs and then other few requests to get information about each author. With GraphQL this can be done in single query:

1// a GraphQL query
2articles {
3 edges {
4 title
5 author {
6 fullname
7 email
8 }
9 }
10}
11
12// a GraphQL query result
13{
14 "data": {
15 "articles": {
16 "edges": [
17 {
18 "title": "Article title",
19 "author": {
20 "fullname": "author fullname",
21 "email": "test@example.com"
22 }
23 },
24 {
25 "title": "Article title",
26 "user": {
27 "fullname": "author fullname",
28 "email": "test@example.com"
29 }
30 },
31 {
32 "title": "Article title",
33 "user": {
34 "fullname": "author fullname",
35 "email": "test@example.com"
36 }
37 }
38 ]
39 }
40 }
41}

In order to understand better how GraphQL works, I made a simple app. GraphQL Apollo Server is acting like an interface between MongoDB plus external API and the client. As client-side consumer I've chosen Angular, but it can be any other solution which Apollo Client is supporting.

Robin Wieruch has done a great job explaining all details about how to setup correctly Apollo GraphQL in his lengthy and throughout tutorial. I will focus on main aspects of implementation.

On server I have defined schema:

1import {gql} from 'apollo-server-express'
2
3export default gql`
4 extend type Query {
5 task(name: String!): Task
6 getAllTasks: [Task]
7 }
8 extend type Mutation {
9 addTask(name: String): Task!
10 updateTask(id: String, name: String): Task
11 removeTask(id: String): Task
12 }
13 type Task {
14 id: String
15 name: String
16 }
17`

And resolver:

1export default {
2 Query: {
3 getAllTasks: async () => {
4 const tasks = await Task.find()
5 const issues = await getIssues()
6
7 return [...tasks, ...issues]
8 },
9 task: (parent, arg) => {
10 return {name: arg.name}
11 },
12 },
13 Mutation: {
14 addTask: (parent, {name}) => {
15 return new Promise((resolve, reject) => {
16 Task.create({name}, (err, result) => {
17 const task = R.pipe(
18 R.pickAll(['_id', 'name']),
19 renameKeys({_id: 'id'}),
20 )(result.toJSON())
21
22 if (err) return reject(err)
23
24 resolve({id: task.id.toString(), name: task.name})
25 })
26 })
27 },
28 removeTask: async (parent, {id}) => {
29 try {
30 const removedtask = await Task.findByIdAndRemove(id).exec()
31
32 return removedtask
33 } catch (e) {
34 throw new Error('Error: ', e)
35 }
36 },
37 updateTask: async (parent, {id, name}) => {
38 try {
39 const updatedTask = await Task.findByIdAndUpdate(id, {
40 $set: {name},
41 }).exec()
42
43 return updatedTask
44 } catch (e) {
45 throw new Error('Error: ', e)
46 }
47 },
48 },
49}

On client we consume GraphQL via queries and mutations:

1import gql from 'graphql-tag'
2
3export const addTask = gql`
4 mutation addTask($name: String!) {
5 addTask(name: $name) {
6 id
7 name
8 }
9 }
10`
11
12export const getAllTasks = gql`
13 query {
14 getAllTasks {
15 id
16 name
17 }
18 }
19`
20
21export const removeTask = gql`
22 mutation removeTask($id: String!) {
23 removeTask(id: $id) {
24 id
25 name
26 }
27 }
28`
29
30export const updateTask = gql`
31 mutation updateTask($id: String!, $name: String!) {
32 updateTask(id: $id, name: $name) {
33 id
34 name
35 }
36 }
37`

All code is available here.

Vadim Nicolai

Vadim Nicolai is a JavaScript software engineer.