DEV Community

The Javascript Ninja
The Javascript Ninja

Posted on • Originally published at thejavascriptninja.com

The Ultimate Beginner’s Guide to GraphQL: Part 2 – Organization, Parameters, more Queries, and Mutations

Hi everyone! Welcome back to part 2 of The Ultimate Beginner’s Guide to GraphQL tutorial series. Before we start, if you haven’t seen part 1, you might want to check it out here. This tutorial will build off the concepts learned in part 1.

With all that said, I wanted to go more in-depth about something I only briefly mentioned in part 1: the GraphQL infrastructure. This is an important part of learning how GraphQL works, and what makes it so awesome.

The GraphQL Infrastructure

To get a better understanding of the advantages and disadvantages of GraphQL, I created this handy little infographic:

graphql_infographic

Alright! Hopefully that gives you a little insight into the inner workings of GraphQL, and helps you understand some of the operations at a deeper level.

Organizing the Code

Unfortunately, before we get to the fun stuff in this tutorial, we do have to work through the boring stuff. This means working on organizing our code.

If you don’t remember, we used our standard server code and made some significant changes to the index.js file in part 1 of the tutorial. I’d recommend reading that part first so you’ll be up-to-date on what we’re doing. After completing part 1, the code in our index.js file should look like this:

const port = process.env.port || 3000;
const express = require('express');
const ejs = require('ejs');
const layouts = require('express-ejs-layouts');
const app = express();

app.set('view engine', 'ejs');
app.use(express.static('public'));
app.use(layouts);

const homeController = require('./controllers/homeController.js');
app.get('/', homeController.renderIndex);

const { gql } = require('apollo-server-express');
const schema = gql`
  type Query {
    getUsers: User
  }
  type User {
    id: Int!
    username: String!
    email: String!
    password: String!
  }
`;

let users = [
  {
    id:1,
    username:'The Javascript Ninja',
    email:'contact@thejavascriptninja.com',
    password:'its-a-secret'
  },
  {
    id:2,
    username:'The Javascript Ninjas Best Friend',
    email:'contact@thejavascriptninja.com',
    password:'its-a-secret'
  },
]

const resolvers = {
  Query: {
    getUsers: _ => 'Hello World'
  }
}

const { ApolloServer } = require('apollo-server-express');
const serve = new ApolloServer({
  typeDefs: schema,
  resolvers: resolvers,
});
serve.applyMiddleware({ app });

const server = app.listen(port, () => {
  console.log(`🚀 Server listening on port ${port}`);
});
Enter fullscreen mode Exit fullscreen mode

For a full explanation, read part 1. Otherwise, note that the code above will create a GraphQL server alongside our express server, and define a simple query to execute. Don’t leave yet – read on. We’ll be expanding this database model later in the tutorial.

Since we’ll be greatly expanding the code in our index.js file, it might do us some good to split it up between other files. This will reduce the amount of clutter in our main file, and keep our project file organized.

To organize our code, we can move our schema and resolvers objects to separate files. This may seem a little overkill at first, but after we expand them, it will be totally necessary.

To do this, first, create a models folder at the root level of your project. We will still want everything in our project to correspond with MVC formatting.

Then, in our new models folder, we’ll create the files schema.js and resolvers.js.

Next, in schema.js, cut and paste the schema object from index.js:

const { gql } = require('apollo-server-express');
const schema = gql`
  type Query {
    getUsers: User
  }
  type User {
    id: Int!
    username: String!
    email: String!
    password: String!
  }
`;
Enter fullscreen mode Exit fullscreen mode

Then, in resolvers.js, cut and paste the resolvers object and users array from index.js:

let users = [
  {
    id:1,
    username:'The Javascript Ninja',
    email:'contact@thejavascriptninja.com',
    password:'its-a-secret'
  },
  {
    id:2,
    username:'The Javascript Ninjas Best Friend',
    email:'contact@thejavascriptninja.com',
    password:'its-a-secret'
  },
];

const resolvers = {
  Query: {
    getUsers: _ => users;
  }
}
Enter fullscreen mode Exit fullscreen mode

Then, modify index.js so it looks like this:

const port = process.env.port || 3000;
const express = require('express');
const ejs = require('ejs');
const layouts = require('express-ejs-layouts');
const app = express();

app.set('view engine', 'ejs');
app.use(express.static('public'));
app.use(layouts);

const homeController = require('./controllers/homeController.js');
app.get('/', homeController.renderIndex);

const schema = require('./models/schema.js');
const resolvers = require('./models/resolvers.js');

const { ApolloServer } = require('apollo-server-express');
const serve = new ApolloServer({
  typeDefs: schema,
  resolvers: resolvers,
});
serve.applyMiddleware({ app });

const server = app.listen(port, () => {
  console.log(`🚀 Server listening on port ${port}`);
});
Enter fullscreen mode Exit fullscreen mode

Awesome! Now our code is all nice and organized. All that we’ve done above is just sort our resolvers and schema objects into modules so they don’t all clutter up the same file.

Writing More Advanced Queries With Parameters

Alright, now it’s time to work on the meat of GraphQL: querying. Querying is arguably the biggest and most important part of GraphQL (partly because the QL stands for Query Language). But, with all that said, it’s time to focus on writing more advanced query functions. The queries we wrote in part 1 were great, but they couldn’t do much and left a lot to be desired.

In a realistic situation, your GraphQL query is probably going to return a lot of data. There are multiple reasons why this could be bad:

• It’s hard to comprehend
• It will drastically slow down the site
• It’s impossible to filter through or perform operations on

As you can see, none of these options are good in the slightest. That’s why it’s important to write better queries by filtering through data to return only what we need, not the entire database. We can do this by adding query parameters.

Adding In Some Parameters

To add some parameters to our query, navigate to your schema.js file.

Then, let’s add some parameters to the getUsers query in the Query type.

const { gql } = require('apollo-server-express');
const schema = gql`
  type Query {
    getUsers(id:Int, username:String, email:String, password:String): User
  }
  type User {
    id: Int!
    username: String!
    email: String!
    password: String!
  }
`;
Enter fullscreen mode Exit fullscreen mode

As you can see, we added all the arguments we wanted available to the getUsers query. The reason I’ve added these arguments is so I’ll be able to filter through different users by these specific fields. There are no exclamation points after the object types in the parameters because I want all parameters to be optional.

However, before we can successfully execute our query with these parameters, we need to make some edits to our resolvers.

Go to resolvers.js. Let’s update our getUsers resolver. Right now, it looks like this:

let users = [
  {
    id:1,
    username:'The Javascript Ninja',
    email:'contact@thejavascriptninja.com',
    password:'its-a-secret'
  },
  {
    id:2,
    username:'The Javascript Ninjas Best Friend',
    email:'contact@thejavascriptninja.com',
    password:'its-a-secret'
  },
];

const resolvers = {
  Query: {
    getUsers: _ => 'Hello World'
  }
}
Enter fullscreen mode Exit fullscreen mode

Pretty lame, huh? Right now, this lame resolver can only return our pre-set array of objects. And even at that, we can’t even filter the results in our query.

Well, it’s time for things to change. Update resolvers.js so it looks like the following:

let users = [
  {
    id:1,
    username:'The Javascript Ninja',
    email:'contact@thejavascriptninja.com',
    password:'its-a-secret'
  },
  {
    id:2,
    username:'The Javascript Ninjas Best Friend',
    email:'contact@thejavascriptninja.com',
    password:'its-a-secret'
  },
];

const resolvers = {
  Query: {
    getUsers: (parent, args) => {
      if (args.id) {
        return users.filter(user => user.id === args.id);
      } else if (args.username) {
        return users.filter(user => user.username === args.username);
      } else if (args.email) {
        return users.filter(user => user.email === args.email);
      } else if (args.password) {
        return users.filter(user => user.password === args.password);
      } else {
        return users;
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Wow – Big improvement. However, there’s a lot going on; let me explain it for you:

1. First, the getUsers method takes in two parameters: parent, and args. It is important that args be the second parameter, otherwise you will get an error.

2. Second, we make a long if statement. First, we check if the arguments id, username, email, or password were provided to filter through the data. If no parameters were provided, we return all the data in the users array.

3. If parameters were provided with the query, we filter through the data in the users array with the array.filter() method. Then, we return matching data – if there is any.

Now, let’s test our new-and-improved query. Run your server and navigate to localhost:3000/graphql.

img

Then, enter the following query into the box on the left:

query {
  getUsers(id:1) {
    id
    username
    email
    password
  }
}
Enter fullscreen mode Exit fullscreen mode

This should retrieve all data for the user with an id equal to 1.

img

GraphQL Mutations

The next big part of GraphQL is modifying data in the database. This includes adding and deleting users, in our scenario. Fortunately, GraphQL provides an easy way for us to do this: mutations.

In a brief summary, mutations are just like GraphQL queries, except they modify data. In order to make a mutation, we can define a mutation type just like we did a Query type in our schema.

Modify your schema in schema.js to look like the following:

const { gql } = require('apollo-server-express');
const schema = gql`
  type Query {
    getUsers(id:Int, username:String, email:String, password:String): User
  }
  type Mutation {
    createUser(username:String, email:String, password:String): User
  }
  type User {
    id: Int!
    username: String!
    email: String!
    password: String!
  }
`;
Enter fullscreen mode Exit fullscreen mode

As you can see, mutations don’t look very different from queries. Of course, you can always get more advanced; these mutations are on a very basic level.

In the Mutation type above, we define a createUser mutation. This mutation takes in 3 parameters: username, email, and password. The reason we’re not going to provide the id property is because we want the id to be defined by the computer, whether randomly or in order, not manually.

In order to put our mutation into effect, we’ll need to make some edits to our resolvers. Look at the new resolvers below:

let users = [
  {
    id:1,
    username:'The Javascript Ninja',
    email:'contact@thejavascriptninja.com',
    password:'its-a-secret'
  },
  {
    id:2,
    username:'The Javascript Ninjas Best Friend',
    email:'contact@thejavascriptninja.com',
    password:'its-a-secret'
  },
];
const resolvers = {
  Query: {
    getUsers: (parent, args) => {
      if (args.id) {
        return users.filter(user => user.id === args.id);
      } else if (args.username) {
        return users.filter(user => user.username === args.username);
      } else if (args.email) {
        return users.filter(user => user.email === args.email);
      } else if (args.password) {
        return users.filter(user => user.password === args.password);
      } else {
        return users;
      }
    }
  },
  Mutation: {
    createUser: (parent, args) => {
      let newUser = {
        id: users.length + 1,
        username: args.username,
        email: args.email,
        password: args.password
      };
      users.push(newUser);
      return newUser;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

As you can see, in our resolver, we have a new property after the Query property! This is the Mutation property. In the Mutation property, we have the createUser method. This is so far our first mutation. In the createUser method we do 3 things:

1. Create a newUser object. In this object, we set the id, username, email, and password of our new user.

  1. Add the newUser object to the database. In reality, we would add the newUser to the database. However, since we’re just using a dummy database model, we just use array.push() to add the newUser to our users array.

  2. Return the newUser. This is pretty straightforward. We just return the newUser object as a result of the mutation.

Running our First Mutation

Now that we’ve got our mutation all done, it’s time to run. (Yes, that rhymes 😀). In order to run our mutation, start your server and navigate to localhost:3000/graphql.

img

To make sure everything is working right, let’s first run our query. Enter this query into the box on the left:

query {
  getUsers {
    id
    username
    email
    password
  }
}
Enter fullscreen mode Exit fullscreen mode

You should see the following result:

img

Now that we’ve ensured everything works and we aren’t getting any errors, it’s time to test our mutation. Let’s enter the mutation we wrote earlier:

mutation myMutation {
  createUser(username:"Subscriber to TJN", email:"contact@thejavascriptninja.com", password:"secret") {
    id
    username
    email
    password
  }
}
Enter fullscreen mode Exit fullscreen mode

As you can see, we call createUser, and give it the specified params. It should return the following result:

{
  "data": {
    "createUser": {
      "id": 3,
      "username": "Subscriber to TJN",
      "email": "contact@thejavascriptninja.com",
      "password": "secret"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

img

Then, if we run our query again, we can see our mutation has taken effect.

Run:

query {
  getUsers {
    id
    username
    email
    password
  }
}
Enter fullscreen mode Exit fullscreen mode

You should see the following:

{
  "data": {
    "getUsers": [
      {
        "id": 1,
        "username": "The Javascript Ninja",
        "email": "contact@thejavascriptninja.com",
        "password": "its-a-secret"
      },
      {
        "id": 2,
        "username": "The Javascript Ninjas Best Friend",
        "email": "contact@thejavascriptninja.com",
        "password": "its-a-secret"
      },
      {
        "id": 3,
        "username": "Subscriber to TJN",
        "email": "contact@thejavascriptninja.com",
        "password": "secret"
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

img

Hooray! Our mutation worked!

To Conclude…

A’ight folks!

Today we’ve talked about organizing our code, writing more advanced queries (using parameters and variables), and GraphQL mutations.

Pretty awesome stuff.

I am going to end the tutorial here so it doesn’t get too long, but make sure to subscribe so you don’t miss out on any great content!

Stay tuned and talk soon!

Top comments (0)