Skip to main content

Building a GraphQL API with Node and MongoDB

 Over the past decade, REST has become the standard for designing web APIs. REST has become more concrete with emerging best practices for the web. However, it has become too inflexible to keep up with the complex client side requirements. As a result, there is more data fetching happening behind the scene and REST has performed poorly with this growth. To overcome the inflexibility and inefficiency, GraphQL was created.

In this article, we will build a restaurant app that tracks chefs and dishes that can be queried and updated using GraphQL

GraphQL is a query language created by Facebook with the purpose of building robust client applications based on intuitive and flexible syntax. It fully describes the data requirements and interactions with an existing database. GraphQL was developed internally by Facebook and released in 2015.

A GraphQL query is a string that is sent to a server to be interpreted and fulfilled, and the response returns JSON back to the client.

With traditional REST API calls, we didn’t have the ability for the client to request a customized set of data. In contrast, GraphQL allows clients to define the structure of the data required, and the same structure of the data is returned from the server. This prevents excessively large amounts of data from being returned. However, it also adds a layer of complexity that may not be applicable for simple APIs.

Moreover, maintaining multiple endpoints is difficult in REST architecture. When the application grows, the number of endpoints will increase, resulting in the client needing to ask for data from different endpoints. GraphQL APIs are more organized by providing structured types and fields in the schema while using a single API endpoint to request data.

Let’s start developing. First we will create a new folder and initialize our package.json file. Then add following packages with the command listed below:

yarn init
yarn add express graphql express-graphql mongoose

Now we can move on to creating our main file app.js in our root directory and require graphqlHTTP from the Express GraphQL package.

const express = require('express');
const graphqlHTTP = require('express-graphql');

const mongo = require('mongoose');
const app = express();
mongo.connect('mongodb://***yourusername***:***yourpassword***@ds053317.mlab.com:53317/gql-demo', {
useNewUrlParser: true,
useUnifiedTopology: true
})

mongo.connection.once('open', () => {
console.log('connected to database');
})

app.use(‘/graphiql’, graphqlHTTP({ schema: require(‘./schema.js’), graphiql: true}));

app.listen(8080, () => {
console.log('Server running succefully...')
})

Here we have required express and graphqlHTTP from our installed packages. We also made our connection with our MongoDB database using mlab. By setting true to graphiql, we can send and receive requests from the browser alike Insomnia or Postman. We also can serve it locally and test it at http://localhost:8080/graphiql to use the console.

Our next step is building our data models for storing items into our database. We will make a new folder mongo-models, and we will create two files chef.js and dishes.js like below:


const mongo = require('mongoose');
const Schema = mongo.Schema;

const chefSchema = new Schema({
    name: String,
    rating: Number
});

module.exports = mongo.model('Chef', chefSchema);

const mongo = require('mongoose');
const Schema = mongo.Schema;

const dishSchema = new Schema({
    name: String,
    country: String,
    tasty: Boolean,
    chefsId: String
});

module.exports = mongo.model('Dish', dishSchema);


Now we will make a folder and name it Schema.js where we will add types to the code and define our GraphQL API:

const graphql = require('graphql');

const Dish = require('../mongo-models/dish');
const Chef = require('../mongo-models/chef');

const {
GraphQLObjectType,
GraphQLString,
GraphQLBoolean,
GraphQLSchema,
GraphQLID,
GraphQLFloat,
GraphQLList,
GraphQLNonNull
} = graphql;


const DishType = new GraphQLObjectType({
name: 'Dish',
fields: () => ({
id: {
type: GraphQLID
},
name: {
type: GraphQLString
},
tasty: {
type: GraphQLBoolean
},
country: {
type: GraphQLString
},
chefs: {
type: ChefType,
resolve(parent, args) {
return Chef.findById(parent.chefsId)
}
}
})
});

const ChefType = new GraphQLObjectType({
name: 'chefs',
fields: () => ({
id: {
type: GraphQLID
},
name: {
type: GraphQLString
},
rating: {
type: GraphQLFloat
},
dish: {
type: new GraphQLList(DishType),
resolve(parent, args) {
return Dish.find({
chefsId: parent.id
})
}
}
})
});

In the above code, we imported graphql and our Mongo models from our folder. We also gave type definitions to our data types in GraphQL, which is wrapped inside a field function with a fat arrow. In GraphQL we have the resolve function that gets fired when we write some root queries while we’ll see in the next steps.

Inside the resolve function, we get data from the database.

const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
dish: {
type: DishType,
args: {
id: {
type: GraphQLID
}
},
resolve(parent, args) {
return Dish.findById(args.id);
}
},
chefs: {
type: ChefType,
args: {
id: {
type: GraphQLID
}
},
resolve(parent, args) {
return Chef.findById(args.id);
}
},
dishes: {
type: new GraphQLList(DishType),
resolve(parent, args) {
return Dish.find({});
}
},
chefs: {
type: new GraphQLList(ChefType),
resolve(parent, args) {
return Chef.find({});
}
}
}
});

const Mutation = new GraphQLObjectType({
name: 'Mutation',
fields: {
addDish: {
type: DishType,
args: {
name: {
type: new GraphQLNonNull(GraphQLString)
},
country: {
type: new GraphQLNonNull(GraphQLString)
},
tasty: {
type: new GraphQLNonNull(GraphQLBoolean)
}
},
resolve(parent, args) {
let dish = new Dish({
name: args.name,
country: args.country,
tasty: args.tasty,
});
return dish.save();
}
},
addChef: {
type: ChefType,
args: {
name: {
type: new GraphQLNonNull(GraphQLString)
},
rating: {
type: new GraphQLNonNull(GraphQLString)
}
},
resolve(parent, args) {
let chef = new Chef({
name: args.name,
rating: args.rating
});
return chef.save();
}
}
}
})

module.exports = new GraphQLSchema({
query: RootQuery,
mutation: Mutation
});

We have added the above line of code in the schema.js file. Here we added rootquery to get the dish type with an id and also return all lists of dishes with the GraphQLList import. The same goes for chefs.

We also added a Mutation to our objects in GraphQL where we can add a dish to the database with the compulsory field GraphQLnonNull import. In the resolve function, we have returned the new object and saved it to the database. The same goes for the mutation for Chefs.

Finally, we have the exports of our rootquery and the mutation in the end.

That’s it! We now have a working API using GraphQL with Node.

I have also linked my GitHub link here so that you guys can fork and work with the available code — Link here





















Comments

Popular posts from this blog

How to use Ngx-Charts in Angular ?

Charts helps us to visualize large amount of data in an easy to understand and interactive way. This helps businesses to grow more by taking important decisions from the data. For example, e-commerce can have charts or reports for product sales, with various categories like product type, year, etc. In angular, we have various charting libraries to create charts.  Ngx-charts  is one of them. Check out the list of  best angular chart libraries .  In this article, we will see data visualization with ngx-charts and how to use ngx-charts in angular application ? We will see, How to install ngx-charts in angular ? Create a vertical bar chart Create a pie chart, advanced pie chart and pie chart grid Introduction ngx-charts  is an open-source and declarative charting framework for angular2+. It is maintained by  Swimlane . It is using Angular to render and animate the SVG elements with all of its binding and speed goodness and uses d3 for the excellent math functio...

Understand Angular’s forRoot and forChild

  forRoot   /   forChild   is a pattern for singleton services that most of us know from routing. Routing is actually the main use case for it and as it is not commonly used outside of it, I wouldn’t be surprised if most Angular developers haven’t given it a second thought. However, as the official Angular documentation puts it: “Understanding how  forRoot()  works to make sure a service is a singleton will inform your development at a deeper level.” So let’s go. Providers & Injectors Angular comes with a dependency injection (DI) mechanism. When a component depends on a service, you don’t manually create an instance of the service. You  inject  the service and the dependency injection system takes care of providing an instance. import { Component, OnInit } from '@angular/core'; import { TestService } from 'src/app/services/test.service'; @Component({ selector: 'app-test', templateUrl: './test.component.html', styleUrls: ['./test.compon...

How to solve Puppeteer TimeoutError: Navigation timeout of 30000 ms exceeded

During the automation of multiple tasks on my job and personal projects, i decided to move on  Puppeteer  instead of the old school PhantomJS. One of the most usual problems with pages that contain a lot of content, because of the ads, images etc. is the load time, an exception is thrown (specifically the TimeoutError) after a page takes more than 30000ms (30 seconds) to load totally. To solve this problem, you will have 2 options, either to increase this timeout in the configuration or remove it at all. Personally, i prefer to remove the limit as i know that the pages that i work with will end up loading someday. In this article, i'll explain you briefly 2 ways to bypass this limitation. A. Globally on the tab The option that i prefer, as i browse multiple pages in the same tab, is to remove the timeout limit on the tab that i use to browse. For example, to remove the limit you should add: await page . setDefaultNavigationTimeout ( 0 ) ;  COPY SNIPPET The setDefaultNav...