Creating robust, scalable web applications is easier than ever with the power of modern JavaScript tools. In this tutorial, you’ll learn how to build a complete full-stack CRUD (Create, Read, Update, Delete) web application using the latest versions of Node.js, Express.js, React 18, GraphQL, and MongoDB in 2025. We’ll walk through the entire process—from setting up the backend with GraphQL and Express to building a dynamic frontend with React—and demonstrate how these technologies come together to create a seamless developer experience and a high-performing web app.
Prerequisites:
- Node.js (choose recommended version)
- React.js
- Express.js
- GraphQL
- Express-GraphQL
- React Apollo
- Terminal (Mac/Linux) or Node Command Line (Windows)
- IDE or Text Editor (We are using Visual Studio Code)
We assume that you have already installed Node.js. Make sure Node.js command line is working (on Windows) or is runnable in Linux/OS X terminal.
Create an Express.js App
If Express.js Generator hasn't been installed, type this command from the terminal or Node.js command prompt.
sudo npm install express-generator -g
The `sudo` keyword is used in OSX or Linux Terminal; otherwise, you can use that command without `sudo`. Before we create an Express.js app, we have to create a root project folder inside your projects folder. From the terminal or Node.js command prompt, type this command in your project's folder.
mkdir node-react-graphql
Go to the newly created directory.
cd ./node-react-graphql
From there, type this command to generate an Express.js application.
express server
Go to the newly created Express.js app folder.
cd ./server
Type this command to install all required NPM modules described in `package.json` dependencies.
npm install
To check that the Express.js app is running smoothly, type this command.
nodemon
or
npm start
If you see this information in the terminal or command prompt, that means your Express.js app is ready to use.
[nodemon] 3.1.10
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,cjs,json
[nodemon] starting `node ./bin/www`
Install and Configure Mongoose.js Modules for Accessing MongoDB
Why use Mongoose.js as a MongoDB ODM? Mongoose provides a straightforward, schema-based solution to model your application data. It includes built-in type casting, validation, query building, business logic hooks, and more, out of the box. To install Mongoose.js and its required dependencies, type this command.
npm install mongoose --save
Next, open and edit `app.js`, then declare the Mongoose module.
var mongoose = require('mongoose');
Create a connection to the MongoDB server using these lines of code.
mongoose.connect('mongodb://localhost/node-graphql', { promiseLibrary: require('bluebird'), useNewUrlParser: true })
.then(() => console.log('connection successful'))
.catch((err) => console.error(err));
Now, if you re-run again Express.js server after running the MongoDB server or daemon, you will see this information in the console.
[nodemon] 3.1.10
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,cjs,json
[nodemon] starting `node ./bin/www`
connection successful
That means the connection to the MongoDB is successful.
Create a Mongoose.js Model for the Book Document
Before creating a Mongoose.js model that represents a Book Document, we have to create a folder in the server folder to hold Models. After that, we can create a Mongoose.js model file.
mkdir models
touch models/Book.js
Open and edit `server/models/Book.js`, then add these lines of code.
var mongoose = require('mongoose');
var BookSchema = new mongoose.Schema({
id: String,
isbn: String,
title: String,
author: String,
description: String,
published_year: { type: Number, min: 1945, max: 2019 },
publisher: String,
updated_date: { type: Date, default: Date.now },
});
module.exports = mongoose.model('Book', BookSchema);
Install GraphQL Modules and Dependencies
Why use GraphQL? GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools. Next, type this command to install GraphQL modules and it's dependencies.
npm install express express-graphql graphql graphql-date cors --save
Next, open and edit `server/app.js`, then declare all of those modules and dependencies.
var graphqlHTTP = require('express-graphql');
var schema = require('./graphql/bookSchemas');
var cors = require("cors");
The schema is not created yet; we will create it in the next steps. Next, add these lines of code for configuring GraphQL that can be used over HTTP.
app.use('*', cors());
app.use('/graphql', cors(), graphqlHTTP({
schema: schema,
rootValue: global,
graphiql: true,
}));
That's configuration is enabled CORS and GraphiQL. GraphiQL is the user interface for testing GraphQL queries.
Create GraphQL Schemas for the Book
Create a folder in the server folder to hold GraphQL Schema files, then create a JavaScript file for the schema.
mkdir graphql
touch graphql/bookSchemas.js
Next, open and edit `server/graphql/bookSchemas.js`, then declare all required modules and models.
var GraphQLSchema = require('graphql').GraphQLSchema;
var GraphQLObjectType = require('graphql').GraphQLObjectType;
var GraphQLList = require('graphql').GraphQLList;
var GraphQLObjectType = require('graphql').GraphQLObjectType;
var GraphQLNonNull = require('graphql').GraphQLNonNull;
var GraphQLID = require('graphql').GraphQLID;
var GraphQLString = require('graphql').GraphQLString;
var GraphQLInt = require('graphql').GraphQLInt;
var GraphQLDate = require('graphql-date');
var BookModel = require('../models/Book');
Create a GraphQL Object Type for Book models.
var bookType = new GraphQLObjectType({
name: 'book',
fields: function () {
return {
_id: {
type: GraphQLString
},
isbn: {
type: GraphQLString
},
title: {
type: GraphQLString
},
author: {
type: GraphQLString
},
description: {
type: GraphQLString
},
published_year: {
type: GraphQLInt
},
publisher: {
type: GraphQLString
},
updated_date: {
type: GraphQLDate
}
}
}
});
Next, create a GraphQL query type that calls a list of books and a single book by ID.
var queryType = new GraphQLObjectType({
name: 'Query',
fields: function () {
return {
books: {
type: new GraphQLList(bookType),
resolve: function () {
const books = BookModel.find().exec()
if (!books) {
throw new Error('Error')
}
return books
}
},
book: {
type: bookType,
args: {
id: {
name: '_id',
type: GraphQLString
}
},
resolve: function (root, params) {
const bookDetails = BookModel.findById(params.id).exec()
if (!bookDetails) {
throw new Error('Error')
}
return bookDetails
}
}
}
}
});
Finally, export this file as a GraphQL schema by adding this line at the end of the file.
module.exports = new GraphQLSchema({query: queryType});
Add a Mutation for CRUD Operation to the Schema
To complete the CRUD (Create, Read, Update, Delete) operation of GraphQL, we need to add a mutation that contains create, update, and delete operations. Open and edit `server/graphql/bookSchemas.js`, then add this mutation as a GraphQL Object Type.
var mutation = new GraphQLObjectType({
name: 'Mutation',
fields: function () {
return {
addBook: {
type: bookType,
args: {
isbn: {
type: new GraphQLNonNull(GraphQLString)
},
title: {
type: new GraphQLNonNull(GraphQLString)
},
author: {
type: new GraphQLNonNull(GraphQLString)
},
description: {
type: new GraphQLNonNull(GraphQLString)
},
published_year: {
type: new GraphQLNonNull(GraphQLInt)
},
publisher: {
type: new GraphQLNonNull(GraphQLString)
}
},
resolve: function (root, params) {
const bookModel = new BookModel(params);
const newBook = bookModel.save();
if (!newBook) {
throw new Error('Error');
}
return newBook
}
},
updateBook: {
type: bookType,
args: {
id: {
name: 'id',
type: new GraphQLNonNull(GraphQLString)
},
isbn: {
type: new GraphQLNonNull(GraphQLString)
},
title: {
type: new GraphQLNonNull(GraphQLString)
},
author: {
type: new GraphQLNonNull(GraphQLString)
},
description: {
type: new GraphQLNonNull(GraphQLString)
},
published_year: {
type: new GraphQLNonNull(GraphQLInt)
},
publisher: {
type: new GraphQLNonNull(GraphQLString)
}
},
resolve(root, params) {
return BookModel.findByIdAndUpdate(params.id, { isbn: params.isbn, title: params.title, author: params.author, description: params.description, published_year: params.published_year, publisher: params.publisher, updated_date: new Date() }, function (err) {
if (err) return next(err);
});
}
},
removeBook: {
type: bookType,
args: {
id: {
type: new GraphQLNonNull(GraphQLString)
}
},
resolve(root, params) {
const remBook = BookModel.findByIdAndRemove(params.id).exec();
if (!remBook) {
throw new Error('Error')
}
return remBook;
}
}
}
}
});
Finally, add this mutation to the GraphQL Schema exports like below.
module.exports = new GraphQLSchema({query: queryType, mutation: mutation});
Test GraphQL using GraphiQL
To test the queries and mutations of CRUD operations, re-run the Express.js app, then open the browser. Go to this address `http://localhost:3000/graphql` to open the GraphiQL User Interface.
To get the list of books, replace all of the text on the left pane with this GraphQL query, then click the Play button.
To get a single book by ID, use this GraphQL query.
{
book(id: "5c738dd4cb720f79497de85c") {
_id
isbn
title
author
description
published_year
publisher
updated_date
}
}
To add a book, use this GraphQL mutation.
mutation {
addBook(
isbn: "12345678",
title: "Whatever this Book Title",
author: "Mr. Bean",
description: "The short explanation of this Book",
publisher: "Djamware Press",
published_year: 2019
) {
updated_date
}
}
You will see the response in the right pane like this.
{
"data": {
"addBook": {
"updated_date": "2019-02-26T13:55:39.160Z"
}
}
}
To update a book, use this GraphQL mutation.
mutation {
updateBook(
id: "5c75455b146dbc2504b94012",
isbn: "12345678221",
title: "The Learning Curve of GraphQL",
author: "Didin J.",
description: "The short explanation of this Book",
publisher: "Djamware Press",
published_year: 2019
) {
_id,
updated_date
}
}
You will see the response in the right pane like this.
{
"data": {
"updateBook": {
"_id": "5c75455b146dbc2504b94012",
"updated_date": "2019-02-26T13:58:35.811Z"
}
}
}
To delete a book by ID, use this GraphQL mutation.
mutation {
removeBook(id: "5c75455b146dbc2504b94012") {
_id
}
}
You will see the response in the right pane like this.
{
"data": {
"removeBook": {
"_id": "5c75455b146dbc2504b94012"
}
}
}
Install and Create a React.js Application
Open the terminal or Node.js command line, then go to your React.js project's folder.
cd ..
npm create vite@latest client --template react-ts
This command will create a new React app with the name `client`. Next, go to the newly created app folder.
cd client
npm install
Now, run the React app for the first time using this command.
npm run dev
It will automatically open the default browser the point to `http://localhost:5173/`, so the landing page should be like this.
Install and Configure Required Modules and Dependencies
Now, we have to install and configure all of the required modules and dependencies. Type this command to install the modules.
npm install @apollo/client graphql --save
Next, open and edit `client/src/main.tsx`, then add these imports.
import { ApolloClient, ApolloProvider, InMemoryCache } from "@apollo/client";
Instantiate the `ApolloClient` module as a variable before the React.js class name.
const client = new ApolloClient({
uri: "http://localhost:3000/graphql",
cache: new InMemoryCache()
});
Add the `ApolloProvider` to the root of the React.js component.
createRoot(document.getElementById("root")!).render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>
);
Create React.js Router DOM
Before creating React Router DOM, first, we have to install the required NPM modules by typing these commands.
npm install --save react-router-dom
npm install --save-dev bootstrap
The React.js CRUD web application required pages for creating, showing details, and editing Book data. For that, type these commands to create those components.
mkdir src/components
touch src/components/Home.tsx
touch src/components/Create.tsx
touch src/components/Show.tsx
touch src/components/Edit.tsx
Next, we will create routes for those components. Open and edit `src/App.tsx`, then add these imports.
import { BrowserRouter, Route, Routes } from "react-router-dom";
import "../node_modules/bootstrap/dist/css/bootstrap.min.css";
import Home from "./components/Home";
import Edit from "./components/Edit";
import Create from "./components/Create";
import Show from "./components/Show";
Add React Router to the ReactDOM render.
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/edit/:id" element={<Edit />} />
<Route path="/create" element={<Create />} />
<Route path="/show/:id" element={<Show />} />
</Routes>
</BrowserRouter>
);
}
As you can see that Edit, Create, and Show have been added as separate components. Bootstrap is also included in the import to make the views better.
Create a React Component to Display a List of Books
We need to add `graphql-tag` to use GraphQL queries with React.js. Type this command to install it.
npm install graphql-tag --save
Next, open and edit `client/Home.js`, then replace all imports with these.
import { Link } from "react-router-dom";
import "../App.css";
import { gql, useQuery } from "@apollo/client";
Declare a constant before the class name for the query.
const GET_BOOKS = gql`
{
books {
_id
title
author
}
}
`;
interface Book {
_id: string;
title: string;
author: string;
}
interface GetBooksData {
books: Book[];
}
Replace all render function contents with these.
function Home() {
const { loading, error, data } = useQuery<GetBooksData>(GET_BOOKS, {
pollInterval: 500
});
if (loading) return <>Loading...</>;
if (error) return <>Error! {error.message}</>;
return (
<div className="container">
<div className="panel panel-default">
<div className="panel-heading">
<h3 className="panel-title">LIST OF BOOKS</h3>
<h4>
<Link to="/create">Add Book</Link>
</h4>
</div>
<div className="panel-body">
<table className="table table-stripe">
<thead>
<tr>
<th>Title</th>
<th>Author</th>
</tr>
</thead>
<tbody>
{data?.books.map((book) => (
<tr key={book._id}>
<td>
<Link to={`/show/${book._id}`}>{book.title}</Link>
</td>
<td>{book.author}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
);
}
export default Home;
Create a React Component to Show and Delete Books
As you see in the previous steps, it's a link to show the details of the Book. For that, open and edit `client/components/Show.tsx`, then add these imports.
import React from "react";
import { Link, useParams, useNavigate } from "react-router-dom";
import "../App.css";
import { gql, useQuery, useMutation } from "@apollo/client";
Add the constant variables of query and mutation before the class name.
const GET_BOOK = gql`
query book($bookId: String) {
book(id: $bookId) {
_id
isbn
title
author
description
published_year
publisher
updated_date
}
}
`;
const DELETE_BOOK = gql`
mutation removeBook($id: String!) {
removeBook(id: $id) {
_id
}
}
`;
interface Book {
_id: string;
isbn: string;
title: string;
author: string;
description: string;
published_year: number;
publisher: string;
updated_date: string;
}
interface GetBookData {
book: Book;
}
interface GetBookVars {
bookId: string;
}
interface DeleteBookResult {
removeBook: { _id: string };
}
interface DeleteBookVars {
id: string;
}
Add a constant with the name `Show` as below.
const Show: React.FC = () => {
const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
const { loading, error, data } = useQuery<GetBookData, GetBookVars>(
GET_BOOK,
{
variables: { bookId: id || "" },
pollInterval: 500
}
);
const [removeBook, { loading: deleteLoading, error: deleteError }] =
useMutation<DeleteBookResult, DeleteBookVars>(DELETE_BOOK, {
onCompleted: () => navigate("/")
});
const handleDelete = (e: React.FormEvent) => {
e.preventDefault();
if (data?.book._id) {
removeBook({ variables: { id: data.book._id } });
}
};
if (loading) return <>Loading...</>;
if (error) return <>Error! {error.message}</>;
const book = data?.book;
return (
<div className="container">
<div className="panel panel-default">
<div className="panel-heading">
<h4>
<Link to="/">Book List</Link>
</h4>
<h3 className="panel-title">{book?.title}</h3>
</div>
<div className="panel-body">
<dl>
<dt>ISBN:</dt>
<dd>{book?.isbn}</dd>
<dt>Author:</dt>
<dd>{book?.author}</dd>
<dt>Description:</dt>
<dd>{book?.description}</dd>
<dt>Published Year:</dt>
<dd>{book?.published_year}</dd>
<dt>Publisher:</dt>
<dd>{book?.publisher}</dd>
<dt>Updated:</dt>
<dd>{book?.updated_date}</dd>
</dl>
<form onSubmit={handleDelete}>
<Link to={`/edit/${book?._id}`} className="btn btn-success">
Edit
</Link>
<button type="submit" className="btn btn-danger">
Delete
</button>
</form>
{deleteLoading && <p>Loading...</p>}
{deleteError && <p>Error :( Please try again</p>}
</div>
</div>
</div>
);
};
export default Show;
Create a React Component to Add a New Book
To add a new Book, open and edit `client/components/Create.tsx`, then add these imports.
import React, { useState } from "react";
import { gql, useMutation } from "@apollo/client";
import { Link, useNavigate } from "react-router-dom";
Create a constant variable for the mutation.
const ADD_BOOK = gql`
mutation AddBook(
$isbn: String!
$title: String!
$author: String!
$description: String!
$publisher: String!
$published_year: Int!
) {
addBook(
isbn: $isbn
title: $title
author: $author
description: $description
publisher: $publisher
published_year: $published_year
) {
_id
}
}
`;
interface AddBookInput {
isbn: string;
title: string;
author: string;
description: string;
publisher: string;
published_year: number;
}
interface AddBookResponse {
addBook: {
_id: string;
};
}
Add a constant with its contents like below.
const Create: React.FC = () => {
const navigate = useNavigate();
const [formState, setFormState] = useState<AddBookInput>({
isbn: "",
title: "",
author: "",
description: "",
publisher: "",
published_year: new Date().getFullYear(),
});
const [addBook, { loading, error }] = useMutation<AddBookResponse, AddBookInput>(ADD_BOOK, {
onCompleted: () => navigate("/"),
});
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
setFormState(prev => ({
...prev,
[name]: name === "published_year" ? parseInt(value) : value,
}));
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
addBook({ variables: formState });
};
return (
<div className="container">
<div className="panel panel-default">
<div className="panel-heading">
<h3 className="panel-title">ADD BOOK</h3>
</div>
<div className="panel-body">
<h4>
<Link to="/" className="btn btn-primary">Book List</Link>
</h4>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="isbn">ISBN:</label>
<input
type="text"
className="form-control"
name="isbn"
value={formState.isbn}
onChange={handleChange}
placeholder="ISBN"
/>
</div>
<div className="form-group">
<label htmlFor="title">Title:</label>
<input
type="text"
className="form-control"
name="title"
value={formState.title}
onChange={handleChange}
placeholder="Title"
/>
</div>
<div className="form-group">
<label htmlFor="author">Author:</label>
<input
type="text"
className="form-control"
name="author"
value={formState.author}
onChange={handleChange}
placeholder="Author"
/>
</div>
<div className="form-group">
<label htmlFor="description">Description:</label>
<textarea
className="form-control"
name="description"
value={formState.description}
onChange={handleChange}
placeholder="Description"
cols={80}
rows={3}
/>
</div>
<div className="form-group">
<label htmlFor="publisher">Publisher:</label>
<input
type="text"
className="form-control"
name="publisher"
value={formState.publisher}
onChange={handleChange}
placeholder="Publisher"
/>
</div>
<div className="form-group">
<label htmlFor="published_year">Published Year:</label>
<input
type="number"
className="form-control"
name="published_year"
value={formState.published_year}
onChange={handleChange}
placeholder="Published Year"
/>
</div>
<button type="submit" className="btn btn-success">
Submit
</button>
</form>
{loading && <p>Loading...</p>}
{error && <p>Error :( Please try again</p>}
</div>
</div>
</div>
);
};
export default Create;
Create a React Component to Edit a Book
In the `Show` component, it's a button that edits the Book. Now, we will create a component to edit a book. Open and edit `client/components/Edit.tsx`, then add these imports.
import React, { useState, useEffect } from "react";
import { useParams, useNavigate, Link } from "react-router-dom";
import { gql, useQuery, useMutation } from "@apollo/client";
Add a constant as a query to get and update the book data.
const GET_BOOK = gql`
query book($bookId: String) {
book(id: $bookId) {
_id
isbn
title
author
description
published_year
publisher
updated_date
}
}
`;
const UPDATE_BOOK = gql`
mutation updateBook(
$id: String!
$isbn: String!
$title: String!
$author: String!
$description: String!
$publisher: String!
$published_year: Int!
) {
updateBook(
id: $id
isbn: $isbn
title: $title
author: $author
description: $description
publisher: $publisher
published_year: $published_year
) {
updated_date
}
}
`;
interface Book {
_id: string;
isbn: string;
title: string;
author: string;
description: string;
published_year: number;
publisher: string;
updated_date: string;
}
interface GetBookData {
book: Book;
}
interface GetBookVars {
bookId: string;
}
interface UpdateBookVars {
id: string;
isbn: string;
title: string;
author: string;
description: string;
publisher: string;
published_year: number;
}
Add the constant with the name `Edit` and its contents.
const Edit: React.FC = () => {
const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
const { loading, error, data } = useQuery<GetBookData, GetBookVars>(
GET_BOOK,
{
variables: { bookId: id || "" }
}
);
const [updateBook, { loading: updateLoading, error: updateError }] =
useMutation<{ updateBook: { updated_date: string } }, UpdateBookVars>(
UPDATE_BOOK,
{
onCompleted: () => navigate("/")
}
);
const [formState, setFormState] = useState<UpdateBookVars>({
id: "",
isbn: "",
title: "",
author: "",
description: "",
publisher: "",
published_year: new Date().getFullYear()
});
// Initialize form with book data
useEffect(() => {
if (data?.book) {
const b = data.book;
setFormState({
id: b._id,
isbn: b.isbn,
title: b.title,
author: b.author,
description: b.description,
publisher: b.publisher,
published_year: b.published_year
});
}
}, [data]);
const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
const { name, value } = e.target;
setFormState((prev) => ({
...prev,
[name]: name === "published_year" ? parseInt(value) : value
}));
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
updateBook({ variables: formState });
};
if (loading) return <>Loading...</>;
if (error) return <>Error! {error.message}</>;
return (
<div className="container">
<div className="panel panel-default">
<div className="panel-heading">
<h3 className="panel-title">EDIT BOOK</h3>
</div>
<div className="panel-body">
<h4>
<Link to="/" className="btn btn-primary">
Book List
</Link>
</h4>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="isbn">ISBN:</label>
<input
type="text"
className="form-control"
name="isbn"
value={formState.isbn}
onChange={handleChange}
/>
</div>
<div className="form-group">
<label htmlFor="title">Title:</label>
<input
type="text"
className="form-control"
name="title"
value={formState.title}
onChange={handleChange}
/>
</div>
<div className="form-group">
<label htmlFor="author">Author:</label>
<input
type="text"
className="form-control"
name="author"
value={formState.author}
onChange={handleChange}
/>
</div>
<div className="form-group">
<label htmlFor="description">Description:</label>
<textarea
className="form-control"
name="description"
value={formState.description}
onChange={handleChange}
rows={3}
/>
</div>
<div className="form-group">
<label htmlFor="publisher">Publisher:</label>
<input
type="text"
className="form-control"
name="publisher"
value={formState.publisher}
onChange={handleChange}
/>
</div>
<div className="form-group">
<label htmlFor="published_year">Published Year:</label>
<input
type="number"
className="form-control"
name="published_year"
value={formState.published_year}
onChange={handleChange}
/>
</div>
<button type="submit" className="btn btn-success">
Submit
</button>
</form>
{updateLoading && <p>Updating...</p>}
{updateError && <p>Error :( Please try again</p>}
</div>
</div>
</div>
);
};
export default Edit;
Run and Test GraphQL CRUD from the React.js Application
Before running the whole application, make sure you have run the MongoDB server. To run the MongoDB server manually, type this command in the new Terminal tab.
mongod
Open a new terminal tab, then type this command inside the project folder.
cd server
nodemon
Open a new terminal tab, then type this command inside the project folder.
cd client
npm start
If asking to use a different port, just type `Y`. Now, the browser will automatically open and then show the React.js and GraphQL application like this.
It's the Node, Express, React.js, GraphQL, and MongoDB CRUD Web Application. You can find the full source code on our GitHub.
That's just the basics. If you need more deep learning about MERN Stack, React.js, or React Native, you can take the following cheap course:
- Mastering React JS
- Master React Native Animations
- React: React Native Mobile Development: 3-in-1
- MERN Stack Front To Back: Full Stack React, Redux & Node. js
- Learning React Native Development
Thanks!