Reactnative-app-nodejs-graphql

How to Create Application in React Native with Node.js backend and GraphQL

If you want to go towards fast application development with the backend, then you become confused. What Framework or Technology should I select? Hence you are right to be here.I will explain every topic with an example with minimum complexity that helps you to build a cross-platform application with a backend.

⇒ In this example , we will use React Native(for App), Node.Js(Backend API), & MongoDB(for database).

What is React Native?

If you are new to React Native, then learn from: https://reactnative.dev/

Set up Environment for React Native

I hope you are ready with React Native environment set up, if not then please visit: https://reactnative.dev/docs/environment-setup

How to create a basic API in Node.Js backend?

I will explain this with an example, but you want to know only Node.Js backend API then visit: https://www.section.io/engineering-education/building-a-basic-api-with-nodejs/

Set up Environment for Node.Js

I hope you are also ready with the Node.Js environment setup. It only requires installing node & npm, visits: https://nodejs.org/en/

Set up Environment for MongoDB

Visit MongoDB’s official website & follow the steps to set up MongoDB on your local PC, or you can use the cloud database of MongoDB Alas. visit: https://docs.mongodb.com/manual/installation/

What is GraphQL?

GraphQL is an open-source data query and manipulation language for APIs, and a runtime for fulfilling queries with existing data.

In simple language, if I explain, GraphQL works as a carrier of data between Backend & Frontend, which means we will create API in Node.Js & Application in React Native as usual, but to call Rest APIs we will use GraphQL instead of manual calling with Axios or fetch.  If you want more details, then visit: https://graphql.org/learn/

Start MongoDB, Create backend project, Install required libraries & Implement Backend code

Now we are ready to start after all environments are set up

Here we are going to build an Application with a frontend & backend. So first we will start to develop basic backend APIs for Book & Author. We will not implement complicated things. So let’s start.

So let’s start

Here I am assuming you have completed all the setup. After that, we will follow steps to start MongoDB in the local machine( I am doing all the things in MacOs)

Step-1: Open terminal and this fire below command to in MongoDB directory.

cd /usr/local/var/log/mongodb [if Intel Processor]

cd /opt/homebrew/var/log/mongodb [if Apple M1 Processor]

Step-2: Fire the below command to start the MongoDB database server locally brew services start [email protected] After your work is done, if you want to stop MongoDB then fire the below command in the same path brew services stop [email protected]

Step-3: Here we will start to create APIs in Node.Js & Connect the MongoDB database.

  • Create a folder for your project I named “LibraryM_backend”, you can name anything as you want.
  • Open that folder path in terminal & fire command npm init -y. After this command, you can see a package.json file in your folder. And after that, we will install all required libraries (npm packages)
  • We will install the below packages.
    • npm install mongoose
    • npm install express
  • Create an index.js file in the root directory of a project like “/LibraryM_backend/index.js” and paste the following code.
  • const express = require('express');
    const mongoose = require('mongoose');
    const app = express();
    //Establish database connection
    const connectDb = async () => {
    await mongoose.connect('mongodb://localhost:27017/book_db', {
    useUnifiedTopology: true,
    useNewUrlParser: true
    }).then(() => {
    console.log("DB connected successfully....");
    }).catch(() => {
    console.log("DB connection failed...");
    })
    }
    connectDb()
    //listening server on port 4000
    app.listen(4000, () => {
    console.log("Server is running on port: 4000");
    })
  • mongoose.connect is connecting to the “book_db” MongoDB database. If that is already created then that connects with existing otherwise create a new one & then connect “mongodb://localhost:27017/book_db” is a connection string to connect with the MongoDB database.
  • Run command npm start & you will find in console “Server is running on port: 4000”.
  • Here every time we will need to start the server after changes to get reflect changes, to avoid this we need to install a dev dependency nodemon: npm install –save-dev nodemon
  • Change the following code in package.json
  • "scripts": {
       "test": "echo \"Error: no test specified\" && exit 1",
       "dev": "nodemon server.js"
    },
  • Now we are done with the database connection & server started.

Integrate GraphQL in backend side (Using Apollo Server)

After we will install npm for GraphQL
npm install apollo-server
npm install graphql

Hire-React-Native-Developers - Bigscal

What is Apollo Server?

Apollo Server is an open-source, spec-compliant GraphQL server that’s compatible with any GraphQL client, including Apollo Client. It’s the best way to build a production-ready, self-documenting GraphQL API that can use data from any source.

In simple language, Apollo Server is used in Backend Side and Apollo Client is used in Frontend Side(React JS, React Native, or any other frameworks). So Apollo Client makes it enabled to interact with backend APIs.

blog-image3

Create folder structure for backend

I will create a folder structure like below. You can create according to your convenience.

blog-image4

Configure Apollo Server, Connect to DB & Start Server

Now time to integrate GraphQL with backend in index.js file

const express = require('express');
const mongoose = require('mongoose');
const { ApolloServer } = require('apollo-server-express');
//Resolvers
const BookResolver = require('/src/resolvers/BookResolver');
const AuthorResolver = require('/src/resolvers/AuthorResolver');
const UserResolver = require('/src/resolvers/UserResolver'), ;
//Type Defs
const AuthorTypedef = require('/src/typesDefs/AuthorTypeDef');
const BookTypedef = require('/src/typesDefs/BookTypeDef');
const UserTypedef = require("./src/typesDefs/UserTypedef');

// function to create graphql apollo server
async function startServer() {
const app = express();
const apolloServer = new ApolloServer({
typeDefs: [BookTypeDef, Author TypeDef, UserTypeDef],
resolvers: [BookResolver, AuthorResolver, UserResolver),
context: async ({ reg }) => {
},
});
// Start grapql server
await apolloServer.start();
// Apply express (app) as middleware
await apolloServer.applyMiddleware({ app: app });
// MongoDb database connection
await mongoose.connect('mongodb://localhost:27017/book_db', {
useUnifiedTopology: true,
useNewUrlParser: true
})
console.log("Mongodb connected ........");
app.listen(4000, () => {
console.log("Server is running on port: 4000");
startServer();
})
}
startServer()

Explanation of GraphQL Keywords

In the above code, you can see how GraphQL can be configured & start.

I think you are familiar with express & mongoose, and now ApolloServer. There are three keywords used here “typeDefs” & “resolvers”, the next “context” that will need to be explained.

typeDefs: Document or documents that represent your server’s GraphQL schema.

That needs to pass in typeDefs props while creating ApolloServer. A typeDefs looking like

const { gql } = require('apollo-server-express'),
const BookTypedef = gql`
# AuthorInput
type Author {
id: ID
authorName: String
description: String
email: String
}
# Book
type Book {
id: ID
title: String
bookescription: String
publishedYear: Int
author: Author,
bookImage: File
}
type Query {
getAllBooks: [Book]
getBooksByAuthor(id: ID): [Book]
getSingleBook(id:ID): Book
}
input BookInput {
title: String
bookdescription: String
publishedYear: Int
author: String
}
type Mutation {
createBook (book: BookInput): Book
updateBook(id: String, book: BookInput) : Book
deleteBook (id: String): String
deleteAllBook: String
}
`
module.exports = BookTypeDef

Explanation:

  • “Mutation”:
    • Mutation queries modify data in the data store and return a value. It can be used to insert, update, or delete data. In the above code inside Mutation, there are declared methods, “createBook(book:BookInput): Book” & others for an update, delete, delete all are called “mutation” that is used in resolver where we will perform actual database operation & return data.
  • “resolvers”:
    • A resolver is a function that’s responsible for populating the data for a single field in your schema. This means actual operations are performed( get, or update data in database) in resolver & return response. “Resolvers” are defined in the/src/resolvers folder.
  • “Query”:
    • A GraphQL query is used to read or fetch values, which means to perform the “GET” method, we can use query.
  • “Book” & “Author”:
    • “Book” & “Author” are types that contain which fields are allowed to send as a response. It is user-defined, & we need the “type” keyword to define it.
  • “BookInput”:
    • “BookInput” is an input type that contains what fields we are going to receive in the request body.
  • Note: If we declare any field as “äuthorName”: String! It indicates the required field.
  • // BookModal that defined for MongoDB Book Schema.
    
    const Book = require('../models/BookModal');
    
    const BookResolver = {
    
    Query: {
    
    getAllBooks: async (parent, args, context, info) => {
    
    return await Book.find().populate("author");
    
    },
    
    getSingleBook: async (parent, args, context, info) => {
    
    return await Book.findById(args.id).populate("author");
    
    }
    
    getBooksByAuthor: async (parent, args, context, info) => {
    
    let books = await Book.find({ author: args.id })
    
    return books;
    
    }
    
    }
    
    Mutation: {
    
    createBook: async (parent, args, context, info) => {
    
    const book = new Book(args.book);
    
    await book.save();
    
    return book;
    
    }
    
    updateBook: async (parent, args, context, info) => {
    
    const { id } = args
    
    const book = await Book.findByIdAndUpdate(id, args.book);
    
    return book;
    
    }
    
    deleteBook: async (parent, args, context, info) => {
    
    const { id } = args
    
    await Book.findByIdAndDelete(id)
    
    console.log("Book deleted successfully");
    
    return "Book deleted successfully";
    
    }
    
    deleteAllBook: async (parent, args, context, info) =>{
    
    await Book.deleteMany()
    
    return "Books deleted successfully";
    
    }
    
    }}
    
    module.exports = BookResolver
  • Explanation:
    • It is a resolver for “Book”, in this, we defined Query, & Mutation’s methods that were declared in typeDefs. You can understand better by looking at the above image, let’s see.
  • Mutation: {
    
    createBook: async (parent, args, context, info) => {
    
    const book = new Book(args.book);
    
    await book.save();
    
    return book;
    
    }
    
    }
  • All parameters (parent, args, context, info) are predefined & are explained very well in the doc: https://graphql.org/learn/execution/
  • The operations which were performed in the controller to insert or update data into the database, perform here in the resolver for particular operations.
    • : Hereafter save data into the database, it is returning a “book” object & “book” object may have many fields value, but because of graphQL, it will check “Book” type in typeDefs & return only those filed that will match in Book type.
  • Query: {
    
    getAllBooks: async (parent, args, context, info) => {
    
    return await Book.find().populate("author");
    
    }
    
    }
  • Here all methods that are declared in typeDefs’ Query are defined. It performs fetch operations & returns “Book” objects with only those fields which are declared in “Book” type in typeDefs.
  • “Configure GraphQL Apollo Server”:
    • : After creating typeDefs & resolver, We will create Apollo Server & start that.
  • const apolloServer = new ApolloServer({
    
    typeDefs: [BookTypeDef, Author TypeDef, UserTypeDef],
    
    resolvers: [BookResolver, AuthorResolver, UserResolver),
    
    context: async ({ reg }) => {
    
    },
    
    });
  • in the above image, you can see “context”. It can be used to handle authentication-based API calls. This means inside that we can verify user credential & header’s auth token before going in the resolver, & can throw Authentication error from here, and in the resolver, there is a parameter “context” that contains login user information. We can throw errors if not get user data.

Creating React Native project & Implement GraphQL (Apollo Client)

  • Step-1: Now finally, We are done with the backend all stuff
    • Create a React Native app : npx react-native init LibraryM_frontend
  • Step-2: Open App in VS code & install required npm packages
  • Step-3: You can create a folder structure like below.

Create folder structure for React Native app (Frontend)

blog-image11

Write queries & mutations in the book.js file, that will be used to fetch & update data.

The below image represents “query” which means fetch operations.

Integrate & Configure GraphQL in React Native App (Apollo Client)

import { gql, useMutation } from '@apollo/client'

// Queries
const GET_ALL_AUTHOR = gql`
query getAllAuthors {
getAllAuthors {
id
authorName
description
email
}
}
`;

const GET_SINGLE_AUTHOR = gql`
query getSingleAuthor {
getSingleAuthor {
id
authorName
description
email
}
}
`;

The below image represents the insert operation, similarly, we can write for the update & delete.

import {gql} from '@apollo/client'
// Mutations
const ADD_NEW_AUTHOR = gql`
mutation createAuthor($authorName:String, $description:String, $email:String) {
createAuthor(author:{
authorName: $authorName,
description: $description,
email: $email
}) {
id
authorName
description
email
}
}
`;

const UPDATE_AUTHOR = gql`
mutation updateAuthor($id: String, $authorName:String, $description:String, $email:String) {
updateAuthor(id:$id, author:{
authorName: $authorName,
description: $description,
email: $email
}) {
id
authorName
description
email
}
}
`;

const DELETE_AUTHOR_BY_ID = gql`
mutation deleteAuthor($id: String) {
deleteAuthor(id: $id)
}
`;

const DELETE_ALL_AUTHOR = gql`
mutation deleteAllAuthor {
deleteAllAuthors
}
`;

export {
GET_ALL_AUTHOR,
GET_SINGLE_AUTHOR,
ADD_NEW_AUTHOR,
UPDATE_AUTHOR,
DELETE_AUTHOR_BY_ID,
DELETE_ALL_AUTHOR
}

Configure Apollo client in App.js file

import React from 'react';
import { ApolloClient, createHttpLink, InMemoryCache, ApolloProvider} from '@apollo/client';
import RootNavigation from './src/RootNavigation';

//create http link based on base url
const httpLink = createHttpLink({
uri: 'http://192.168.1.34:4000/graphql',
});

//Create apollo client object
const client = new ApolloClient({
link: httpLink ,
cache: new InMemoryCache(),
// credentials:'include'
});

export default function App() {
return (
<ApolloProvider client={client}>
<RootNavigation />
</ApolloProvider>
)
}
  • Explanation:
    • In the above image “http://192.168.1.34” is the IP of my backend server PC & “4000” is port, “/graphql” is the default URL that is automatically added if you don’t specify in “apolloServer.applyMiddleware({})” “path” param ( It is part of backend )
  • await apolloServer.start();
    await apolloServer.applyMiddleware({ app: app, path:"/graphql" })
  • <ApolloProvider client={client}/>: That wraps our root component, Hence all child components or screens can access the “client”‘s instance & using that we can perform operations of fetch & update.
  • import React, { useState } from 'react'
    import { useMutation, useQuery, useApolloClient } from '@apollo/client';
    import { View, Text, Flatlist, SafeAreaView, Alert, Image } from 'react-native'
    import { GET_ALL BOOKS }_from '../graphQLAPI/book';
    
    export default function Home (props) {
    // local states
    const_[books, setBooks] = useState( []),
    // get apollo client instance
    const client = useApolloclient();
    //fetch book data
    const { loading, error, data } = useQuery(GET_ALL_BOOKS),
    // delete book data
    const [deleteBook, { data: deletedData, loading: deleteLoading, error: deleteError }] = useMutation (DELETE_BOOK_BY_ID);
    return(
    <SafeAreaView style={{ flex: 1, backgroundColor: "#E8F3F3"}}>
    <FlatList
    data={data?.getAllBooks || []} // data.getAllBooks returns all book data
    renderItem={_renderItem}
    keyExtractor={(item, index) => index.toString() }
    refreshing={true}
    />
    </SafeAreaView>
    )
    }
  • “useQuery()”: Above example fetch books data from backend using “useQuery()” hook.
  • And “data.getAllBooks” contains all book data here “getAllBooks” is declared in /graphQLAPI/book.js
  • // Queries
    const GET_ALL_AUTHOR = gql`
    query getAllAuthors {
    getAllAuthors {
    id
    authorName
    description
    email
    }
    }
    `;
  • useMutation()” : This apollo client hook is used to perform insert, update, delete operations. The below image represents how that works
  • blog-image18
  • blog-image19

I hope the above code makes it clear to understand how we can insert data using the “useMutation” hook.

Conclusion

GraphQL makes it easy & efficient to fetch & update data. It can be integrated with your app easily. And also provides benefits of graphQL features.

At Bigscal Technologie, you can Hire React Native Developers & Offshore React Native Developers And Save Upto 60% On Costs And Time, With No Hiring Fees.


8 replies
  1. link login s128
    link login s128 says:

    Hey just wanted to give you a quick heads up and let you know a
    few of the images aren’t loading correctly. I’m not sure why but
    I think its a linking issue. I’ve tried it in two different internet browsers and both show the same outcome.

  2. Penni
    Penni says:

    Hi! I could have sworn I’ve been to this website before but after browsing
    through some of the post I realized it’s new to me. Anyways,
    I’m definitely happy I found it and I’ll be book-marking and
    checking back frequently!

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply