Tutorial 2 : Basic App In GraphQL

Hi there, we are going to create very simple graphQL project in NodeJS. Here I am not going to discuss about what is graphQL as we discussed it earlier ( Tutorial 1: Overview of GraphQL ). Without talking further, let’s move to the project.  

Prerequisites: better have NodeJS basic knowledge.

Step 01: initialize the project

  • Create a folder called ‘gql_demo’.
  • Open it in Visual Code and open terminal.
  • Hit npm init and provide demo project details (not necessary) and simply hit enter.  

Step 02: add project files and config package.json

  • Add server.js file
  • Add schema.js file
  • Add resolver.js file
  • Change package.json as given below.
{
  "name": "gql_demo",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": {
    "dev": "nodemon server.js"
  },
  "author": "",
  "license": "ISC"
}

Step 03: Install npm packages

We need following package to complete our graphQL project. So, let’s install them.

  • Nanoid – to generate random id.
  • GraphQL – to handle graphQL things.
  • Express – for server configuration.
  • Express-graphql – for handling graphQL https.

Command for install npm packages.

npm i express graphql nanoid express-graphql

Install nodemon globally

npm i -g nodemon

Project structure will be like below.

Step 04: server file configuration Add below given code to server.js file.

const express = require('express');
const app = express();
// get route
app.get('/', (req, res) => {
    res.send('It is working');
});
app.listen(3000, () => {
    console.log('Server up and running');
});
  • Run server file using npm run dev command.
  • Open web browser and check ‘http://localhost:3000/’ url, you should see ‘it is working’ text on your browser.

Incase if you are getting an error, make sure 3000 port is not running with any other application.

Step 05: schema.js

  • Before start coding, you have to import grapql npm package into shema.js.
const graphql = require('graphql');

Now we can start coding our schema. Here we are going to create relationship between book and author. Each author can have one or many books. So, we are not going to create foreign keys primary keys like SQL or MySQL. Remember GraphQL is not a query language for database. It is completely for APIs. Here it is pretty easy. We will be using build schema function to develop our entire schema. Enough talking, let’s code.

Initialize the schema as below.

const schema  =graphql.buildSchema(``);
  • Type Autor
const schema = graphql.buildSchema(`
    type Author {
        id:String
        authorName:String!
        authorCountry:String
        language:String
        book:[Book]
    }
`

The most basic components of a GraphQL schema are object types, which just represent a kind of object you can fetch from your service, and what fields it has. In the GraphQL schema language, we might represent it like this:

graphql.org
type Character {
  name: String!
  appearsIn: [Episode!]!
}

That is how GgraphQL org introduce this type and field system. Let’s make it clearer. Type is a keyword that is used to define an object. In our schema Author is an object that is included properties like id, author name, author country etc.

“authorName:String!”

  • authorName is an attribute.
  • String is the datatype of that attribute
  • “String!” this means author name is not a nullable field. It is a required field.

id:String

  • Here, I am using ID as string because we are using nonoid to generate the id. It will generate random string as ID. If you are passing an ID from query you can use id like id:ID

“book:[Book]”

Here, array is the data type of book. As you can see it is a book array. This book array indicates that one author can have many books. Don’t worry, we did not code book type yet😀 .  Here it is.

type Book {
        id:String
        name:String!
        price: Int
        isbn:Int!
       category:Category
    }
enum Category{
        NOVEL
        DRAMA
        POEMS
        BIOGRAPHY
    }

Type book – you can see five properties with two required properties.

“category:Category”

Category is the data type of category property. Here you can only use ‘NOVEL, DRAMA,POEMS and BIOGRAPHY’.

Enum Category – In graphQL we can define enums in this way.

Mutation

It is like a post request in REST API. We can create save data.

If you have an API endpoint that alters data, like inserting data into a database or altering data already in a database, you should make this endpoint a Mutation rather than a Query. This is as simple as making the API endpoint part of the top-level Mutation type instead of the top-level Query type

graphql.org

In our case, we need to save or create author. For that we can use mutation. Remember mutation is a keyword in graphQL

type Mutation{
     createAuthor(input:AuthorInput): Author // Author is the return type
    }

createAuthor(input:AuthorInput): Author

The return type of this mutation is author which we already configured. There is an input type called AuthorInput within brackets that is not configured yet.

Here it is.

 input AuthorInput{
        id:String
        authorName:String!
        authorCountry:String
        language:String
        book:[BookInput]
    }

Here you can see BookInput which is type of array.

input BookInput{
        id:String
        name:String!
        price: Int
        isbn:Int!
        category:Category
    }

We almost completed our schema writing except exporting it. You may know that we have to export our schema file. Otherwise, we will not be able to use it in another place.

module.exports = schema;

Completed Code for Schema.js

const graphql = require('graphql');
const schema = graphql.buildSchema(`
    type Author {
        id:String
        authorName:String!
        authorCountry:String
        language:String
        book:[Book]
    }
    type Book {
        id:String
        name:String!
        price: Int
        isbn:Int!
        category:Category
    }
    enum Category{
        NOVEL
        DRAMA
        POEMS
        BIOGRAPHY
        COMEDIES
    }
    input AuthorInput{
        id:String
        authorName:String!
        authorCountry:String
        language:String
        book:[BookInput]
    }
    input BookInput{
        id:String
        name:String!
        price: Int
        isbn:Int!
        category:Category
    }
    type Query{
        getAuthor(id:String): Author
    }
    type Mutation{
        createAuthor(input:AuthorInput): Author
    }
`);
module.exports = schema;

Step 05: configure resolver

You need to know what is resolver in graphQL. Simply, resolver is set of functions that generate response or results for graphQL query. If you are a beginner, you may wonder about what it graphQL query. Don’t warry we will be talking about that later.

Here I am not going to talk deeper. I just want you to know there is something called resolver which is generate response for the end user. Ok, let’s code our resolver.

const nanoId = require('nanoid').nanoid;
class Author {
    constructor(id, { authorName, authorCountry, language, book }) {
        this.id = id,
            this.authorCountry = authorCountry,
            this.authorName = authorName,
            this.language = language,
            this.book = book
    }
}
  • Here ID is passing as a separate parameter since we are using nanoid npm package to generate it.
  • Create an object for hold author data.
// object for hold authors
const holdAuthors = {};

Now we have to create resolver.

const resolver = {
    //accept id 
    getAuthor: ({ id }) => {
        return new Author(id, holdAuthors[id])
    },
    createAuthor: ({ input }) => {
    let id = nanoId(); // get random id
    holdAuthors[id] = input; //assign id and input into holdAuthors object
    return new Author(id, input); // pass params to author constructor
    }
}
module.exports = resolver;

Remember the getAuthor and createAuthor functions should be same as we defined in schema.js file.

  • createAuthor: this is a mutation that we define in our schema. The purpose of this mutation is creating an author.
  • getAuthor: this is a function that we defined in schema.js where the type is query.
  • Do not forget to export the resolver.

Note that this is not the proper way to code resolver in GraphQL. Remember this, we are developing this application just to know how graphQL basics looks like. We will be developing our next application in formal way with necessary standards.

Completed code for resolver.js

const nanoId = require('nanoid').nanoid;
class Author {
    constructor(id, { authorName, authorCountry, language, book }) {
        this.id = id,
            this.authorCountry = authorCountry,
            this.authorName = authorName,
            this.language = language,
            this.book = book
    }
}
// object for hold authors
const holdAuthors = {};
const resolver = {
    //accept id 
    getAuthor: ({ id }) => {
        return new Author(id, holdAuthors[id])
    },
    createAuthor: ({ input }) => {
     let id = nanoId(); // get random id
     holdAuthors[id] = input; //assign id and input into holdAuthors object
     return new Author(id, input); // pass params to author constructor
    }
}
module.exports = resolver;

Step 06: Let’s move back to the server.js.

Here we have to do some configurations inorder to perform graphQL queries.

  • Import schema and resolver into server.js file.
const resolvers = require('./resolvers');
const schema = require('./schema');
const graphqlHTTP = require('express-graphql').graphqlHTTP;
  • Assign resolvers into root variable.
const root = resolvers;
  • Configure route path for graphQL.
app.use('/graphql', graphqlHTTP({
    schema: schema, // set schema
    rootValue: root, // set root value as our resolvers
    graphiql: true // used to enable graphiql query editor
}));

Completed code for server.js

const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const resolver = require('./resolver');
const schema = require('./schema');
const app = express();
// get route
app.get('/', (req, res) => {
    res.send('It is working');
});
const root = resolver;
app.use('/graphql', graphqlHTTP({
    schema: schema, // set schema
    rootValue: root, // set root value as our resolvers
    graphiql: true // used to enable graphiql query editor
}));
app.listen(3000, () => {
    console.log('Server up and running');
});

Step 07: Let’s run it.

  • We have competed coding part. Let’s run the application using npm run dev. Once you run the application, brows the local URL (http://localhost:3000/graphql) then you should see below given user interface.
  1. Query Editor.
  2. Run query.
  3. Query result (once you execute a query, results will be here).
  4. Documentation explorer (all the queries and mutations will here that we have defined in our schema. By click on query and mutation you can navigate through all the queries and mutations. Just try by clicking on query and mutation).

Step 08: perform queries

Query 01: save author and return author

mutation{
    createAuthor(input: {
    authorName: "William Shakespeare"
    authorCountry: "United Kindom"
    language: "English"
    book: [{
        name: "As You Like It"
        isbn: 448269315
        price: 13
        category: COMEDIES
       },{
        name: "Lover's Complaint"
        isbn: 563289314
        price: 15
        category: POEMS
        }]
    }){
        id,
            authorName,
            authorCountry,
            language,
            book{ id, name, category }
    }
}

Results after perform the query

You can see our query and results here. Code the above query and run it. Look how faster and how easy to access data. Anyway, Let’s get clear idea on our query. As you are noticed, we are calling to createAuthor function using mutation. You already know about mutation right. Yes, Go to out schema.js file and check the createAuthor (createAuthor(input:AuthorInput): Author) function. It takes author input as a parameter and returns author. That is what we exactly got as output.

Check the given figures,

Remember when you execute the query, schema related code will not execute, it is already configured. Only resolver and data source related code will be executed.

Query 02: get author by id

query{
    getAuthor(id: "KpZUTAShHa0krGF-mLJYf") #replace id with your one
    {
        id, authorName, language
        , book{ name, category }
    }
}

As query parameter, we are passing ID how we defined it in the schema and resolver as well. We can give author attribute within the curly braces what we exactly need from the back-end.

Query Result

That is all. I hope you got an idea about graphQL basic operations. There are many more. Keep in touch. 😍

In next application, I hope to do same thing using more formal and standard way which will cover more about concepts that we learnt here.

Download the full application from here. Don’t forget to hit npm install once you cloned the project.

Reference

Featured image

graphql.org

Udara Chinthaka
Author: Udara Chinthaka

Total
1
Shares
1 comment

Leave a Reply

Total
1
Share