Welcome folks today in this blog post we will be building a angular graphql crud api server using mongodb
database in express.js web server at the backend.project All the full source code of the project is shown below.
Get Started
In order to get started you need to make the project folder and then inside that you need to create two folders for frontend
and backend
as shown below
mkdir angulargraphql
cd angulargraphql
mkdir frontend
mkdir backend
Making the GraphQL Backend API in Express
Now we will be building the graphql
backend api using express. For this first of all we will be building a node.js project by executing the below commands as shown below
npm init -y
This will create the package.json file and after that we will install the dependencies for this project
npm i express
npm i cors
npm i express-graphql
npm i graphql
npm i mongoose
Now make a index.js
file inside the root directory of your express.js project and copy paste the below code
index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
const express = require('express'); const bodyParser = require('body-parser'); const mongoose = require('mongoose'); const cors = require('cors'); const { graphqlHTTP } = require('express-graphql'); const graphqlSchema = require('./graphql/schema'); const graphqlResolver = require('./graphql/resolvers'); const app = express(); app.use(bodyParser.json()); app.use(cors()); app.use('/graphql', graphqlHTTP({ schema: graphqlSchema, rootValue: graphqlResolver, graphiql:true, }) ) const dbConfig = require('./config/db.js'); mongoose.Promise = global.Promise; mongoose.connect(dbConfig.url, { useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false, }).then(() => { console.log("Successfully connected to the database"); }).catch(err => { console.log('Could not connect to the database. Exiting now...', err); process.exit(); }); app.listen(3000, () => { console.log("Server is listening on port 3000"); }); |
In this block of code we are initializing a simple express server and starting at port number 3000. And also we are connecting to mongodb database using the database config url which we will set in a different file in the next step. And also we are requiring the graphql api as well and also adding this middleware function to a end point of /graphql
. Inside this we are passing the resolvers and schema of graphql api which we will create in the later steps. And also we are enabling the cors middleware as well which will avoid the cors error.
Connecting to MongoDB Database
Now just make a config
folder and inside it make a db.js
file and copy paste the below code
config/db.js
1 2 3 |
module.exports = { url: 'mongodb://localhost:27017/dbname' } |
Here this is the mongodb database url where the port number is 27017 and also you need to replace the database name also.
Now you need to make a graphql
folder and inside it make a schema.js
file and copy paste the below code
graphql/schema.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
const { buildSchema } = require('graphql'); module.exports = buildSchema(` type Quote { _id: ID! quote: String! author: String! } type QuoteData { quotes: [Quote!]! } input QuoteInputData { quote: String! author: String! } type RootQuery { quotes: QuoteData! } type RootMutation { createQuote(quoteInput: QuoteInputData): Quote! updateQuote(id: ID!, quoteInput: QuoteInputData): Quote! deleteQuote(id: ID!): Quote! } schema { query: RootQuery mutation: RootMutation } `); |
Here we have defined all the schema and the mutations of the graphql api. In the schema we have the different fields which will be there in the graphql api. Also we are providing the datatypes also whether the field is string or number. The mutations means the different CRUD methods which is creation,updation,reading and deletion of records from mongodb database. And lastly we are passing this data to the schema object.
And now we will make the resolver.js
file to actually resolve this mutations and schema which is shown below
graphql/resolver.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
const Quote = require('../models/quote'); module.exports = { quotes: async function () { const quotes = await Quote.find(); return { quotes: quotes.map((q) => { return { ...q._doc, _id: q._id.toString(), }; }), }; }, createQuote: async function ({ quoteInput }) { const quote = new Quote({ quote: quoteInput.quote, author: quoteInput.author, }); const createdQuote = await quote.save(); return { ...createdQuote._doc, _id: createdQuote._id.toString(), }; }, updateQuote: async function ({ id, quoteInput }) { const quote = await Quote.findById(id); if (!quote) { throw new Error('No quote found!'); } quote.quote = quoteInput.quote; quote.author = quoteInput.author; const updatedQuote = await quote.save(); return { ...updatedQuote._doc, _id: updatedQuote._id.toString(), }; }, deleteQuote: async function ({ id }) { const quote = await Quote.findById(id); if (!quote) { throw new Error('No quote found!'); } await Quote.findByIdAndRemove(id); return { ...quote._doc, _id: quote._id.toString(), }; }, }; |
Here as you can see we have defined the implementations of all the mutations which we have declared inside the schema file in the previous system. All the CRUD operations we are performing and for that we are using the mongoose library to directly interact with the mongodb database and perform these operations. For creating a new quote we are taking the values of the quote and the author and then inserting inside the collection in database. And we are using the quote id parameter which is automatically generated by mongodb to actually delete and update the records. And lastly we are using the find() method to read all the quotes which are present inside the collection.
Making the Model for App
Now make a models
folder and inside it make a quote.js file and copy paste the below code
models/quote.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
const mongoose = require('mongoose'); const Schema = mongoose.Schema; const quoteSchema = new Schema({ quote: { type: String, required: true }, author: { type: String, required: true }, }) module.exports = mongoose.model('Quote', quoteSchema); |
As you can see in this block of code we are defining the model for this application. Model is nothing but the data which will be used and also we are defining the datatypes of each field. In this case this is a quote crud application where there will be two fields out there the actual quote and the author name. And lastly we are creating a new collection in mongodb called Quote with the schema defined.
Now if you open the express.js server at localhost:4000/graphql endpoint then you will see the graphql interface where you will filter out data from the api by writing queries.
Making the Frontend of App
Now we will be making the angular frontend for this application. First of all make a new angular project and install the required dependencies which are shown as follows
ng new apollographql
cd apollographql
npm i @apollo/client
npm i apollo-angular
npm i bootstrap
npm i graphql
npm i rxjs
Now we need to modify the app.module.ts
file of your angular project and copy paste the below code
app.module.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; import { GraphQLModule } from './graphql.module'; import { HttpClientModule } from '@angular/common/http'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, GraphQLModule, HttpClientModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { } |
As you can see we are importing the graphqlmodule
and including it in the imports array. For this we need to include the httpclientmodule
and include it in the imports array.
Now you need to create a graphql.module.ts
file and copy paste the following code
graphql.module.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import {NgModule} from '@angular/core'; import {APOLLO_OPTIONS} from 'apollo-angular'; import {ApolloClientOptions, InMemoryCache} from '@apollo/client/core'; import {HttpLink} from 'apollo-angular/http'; const uri = 'http://localhost:3000/graphql'; // <-- add the URL of the GraphQL server here export function createApollo(httpLink: HttpLink): ApolloClientOptions<any> { return { link: httpLink.create({uri}), cache: new InMemoryCache(), }; } @NgModule({ providers: [ { provide: APOLLO_OPTIONS, useFactory: createApollo, deps: [HttpLink], }, ], }) export class GraphQLModule {} |
And now inside this block of code we are importing the apollo client and also we are including the backend api url where the graphql api url is hosted at the backend. And lastly we are creating the Apollo Server by passing the url of the api and also the cache as well.
Now we need to modify the code inside the app.component.html
file and copy paste the below code
app.component.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<mat-toolbar> <span>Angular GraphQL CRUD</span> </mat-toolbar> <div class="flex-row-multicolum"> <div class="flex-col"> <h2>Create Quote</h2> Quote : <input #quote type="text" /><br> Author: <input #author type="text" style="margin-top:5px"/><br> <button style="background-color: #4CAF50; margin-top:5px" (click)="create(quote.value, author.value)">Submit</button> </div> <div class="flex-col"> <h1>Quotes</h1> <ul> <li *ngFor="let quote of quotes | async"> {{ quote.quote }} by {{ quote.author }} <button (click)="delete(quote._id)" style="background-color: red; margin-left:5px">Delete</button> </li> </ul> </div> </div> |
Inside this html block of code we are looping through all the quotes inside the mongodb database. We are looping it inside the li tag and each quote will be displayed and each quote will have the delete button also. When we click the delete button then that quote will be deleted we will be making a post request to the graphql api server and passing the id
parameter. Also we have a simple html5 form where we can create new quotes and insert it into mongodb database.
Now you need to modify the code of app.component.ts file and copy paste the below code
app.component.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
import { Component, OnInit } from '@angular/core'; import { Apollo } from 'apollo-angular'; import gql from 'graphql-tag'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; const GET_QUOTES = gql` { quotes { quotes { _id quote author } } } `; const CREATE_QUOTE = gql` mutation createQuote($quote: String!, $author: String!) { createQuote(quoteInput: { quote: $quote, author: $author }) { _id quote author } } `; const DELETE_QUOTE = gql` mutation deleteQuote($id: ID!) { deleteQuote(id: $id) { _id quote author } } `; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], }) export class AppComponent implements OnInit { title = 'Angular GraphQL CRUD'; quotes: Observable<any>; constructor(private apollo: Apollo) {} ngOnInit() { this.quotes = this.apollo .watchQuery({ query: GET_QUOTES, }) .valueChanges.pipe( map((result: any) => { console.log(result.data.quotes.quotes); return result.data.quotes.quotes; }) ); } create(quote: string, author: string) { this.apollo .mutate({ mutation: CREATE_QUOTE, refetchQueries: [{ query: GET_QUOTES }], variables: { quote: quote, author: author, }, }) .subscribe(() => { console.log('created'); }); } delete(id: string) { console.log(id); this.apollo .mutate({ mutation: DELETE_QUOTE, refetchQueries: [{ query: GET_QUOTES }], variables: { id: id, }, }) .subscribe(() => { console.log('deleted'); }); } } |
And here in this block of code we are using the apollo client where we are building the mutations to actually create the quote and also delete the quote. For this we are also using rxjs/operators.