Jacob Banks

Jacob Banks

1665019396

How to Build a REST API with Node, Express & MySQL Database

Build an example REST API using MySQL as our database, Node.js as our language, and Express.js to parse JSON.

In this tutorial, we’ll learn how to build a REST API using MySQL as our database and Node.js as our language. We’ll also use the Express.js framework to make our task easier. Our example REST API will track the most popular programming languages.

Prerequisites

To follow along with this article, you should have the following:

  • Understanding of how MySQL and relational databases work in general
  • Basic knowledge of Node.js and Express.js
  • Understanding of what REST (representational state transfer) APIs are and how they function
  • Knowledge of what CRUD (create, read, update, delete) is and how it relates to the HTTP methods GET, POST, PUT, and DELETE

The code in this tutorial is performed on a Mac with Node 14 LTS installed. You can also access the full code at the GitHub repository. Let’s get started!

Table of contents

  • What is MySQL?
    • Register on Db4free.net
    • Create the programming languages table
    • Add demo rows for programming languages
  • Setting up Express.js for our REST API
  • REST API project structure
  • GET popular programming languages
  • POST a new programming language
  • PUT to update an existing programming language
  • DELETE a programming language
  • Testing our APIs
  • Further considerations
  • Conclusion

What is MySQL?

MySQL is one of the most popular databases in the world, if not the most popular. Per the 2020 Stack Overflow survey, MySQL was the most-loved database, with more than 55 percent of respondents using it. The community edition is freely available, supported by a large and active community.

MySQL is a feature-packed relational database first released in 1995. MySQL runs on all major operating systems like, Linux, Windows, and macOS. Because of its features and its cost-effectiveness, MySQL is used by big enterprises and new startups alike.

For our example REST API, we’ll use a free MySQL service instead of setting up a local MySQL server. To host our testing MySQL database, we’ll use db4free.net.

Register on db4free.net

To get your free MySQL 8.0 database up and running, you can register on db4free.net. First, go to the db4free signup page, then fill out the required details by choosing your database name and username:

Db4free Signup Page

Click on Signup and you should receive a confirmation email. Confirm your account by clicking on the link in the email. Next, on the sidebar, click on phpMyAdmin. In the phpMyAdmin login, enter the username and password you chose and click Go:

Db3free Registration Options

Create the programming languages table

Now, we have an empty database. Let’s add the programming_languages table. First, click on the database name on the left; for me, it was restapitest123. Then, click SQL on the top menu, which is the second link after Structure, and put the following code for CREATE TABLE in the text area:

CREATE TABLE `programming_languages`
(
  `id`            INT(11) NOT NULL auto_increment ,
  `name`          VARCHAR(255) NOT NULL ,
  `released_year` INT NOT NULL ,
  `githut_rank`   INT NULL ,
  `pypl_rank`     INT NULL ,
  `tiobe_rank`    INT NULL ,
  `created_at`    DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ,
  `updated_at`    DATETIME on UPDATE CURRENT_TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
  PRIMARY KEY (`id`),
  UNIQUE `idx_name_unique` (`name`(255))
)
engine = innodb charset=utf8mb4 COLLATE utf8mb4_general_ci;

Click the Go button, as below:

Create Programming Languages Table

The code will come back with a green check box and a message along the lines of MySQL returned an empty result set (i.e. zero rows).

With that, we’ve created a table called programming_languages with eight columns and a primary key called id, which is an internet and auto-increment. The name column is unique, and we also added the released_year for the programming language. We have three columns to input the rank of the programming language, sourced from the following resources:

The created_at and updated_at columns store dates to keep a track of when the rows were created and updated.

Add demo rows for programming languages

Next, we’ll add 16 popular programming languages to our programming_languages table. Click the same SQL link on the top of the page and copy and paste the code below:

INSERT INTO programming_languages(id,name,released_year,githut_rank,pypl_rank,tiobe_rank) 
VALUES 
(1,'JavaScript',1995,1,3,7),
(2,'Python',1991,2,1,3),
(3,'Java',1995,3,2,2),
(4,'TypeScript',2012,7,10,42),
(5,'C#',2000,9,4,5),
(6,'PHP',1995,8,6,8),
(7,'C++',1985,5,5,4),
(8,'C',1972,10,5,1),
(9,'Ruby',1995,6,15,15),
(10,'R',1993,33,7,9),
(11,'Objective-C',1984,18,8,18),
(12,'Swift',2015,16,9,13),
(13,'Kotlin',2011,15,12,40),
(14,'Go',2009,4,13,14),
(15,'Rust',2010,14,16,26),
(16,'Scala',2004,11,17,34);

You should receive a message that reads something like “16 rows inserted”.

The data collected from our three sources is collected and added to the table in bulk by the INSERT statement, creating 16 rows, one for each programming language. We’ll return to this later when we fetch data for the GET API endpoint.

If we click on the programming_languages table, visible on the left, we’ll see the rows that we just added:

Programming Languages Table Rows Added

Next, we’ll set up Express.js for our REST API with Node.js and MySQL.

Setting up Express.js for our REST API

To set up a Node.js app with an Express.js server, we’ll first create a directory for our project to reside in:

mkdir programming-languages-api && cd programming-languages-api

Then, we can create a package.json file with npm init -y as follows:

{
  "name": "programming-languages-api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

To install Express, we’ll run npm i express, adding Express as a dependency in the package.json file.Next, we’ll create a slim server in the index.js file. It will print an ok message on the main path /:

const express = require("express");
const app = express();
const port = 3000;
app.use(express.json());
app.use(
  express.urlencoded({
    extended: true,
  })
);
app.get("/", (req, res) => {
  res.json({ message: "ok" });
});
app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`);
});

There are a few important things to note in the code above. For one, we’ll using the built-in Express JSON parser middleware to parse JSON in the next steps. We’ll also utilize the express.urlencoded() middleware to parse the URL encoded body.

If the PORT is not provided as an environment variable, our app will run on port 3000. We can run the server with node index.js and hit http://localhost:3000 to see {message: "ok"} as the output.

REST API project structure

We’ll structure our project in the following manner to arrange our files logically in folders:

Node Project Folder Structure Layout

config.js will contain configuration for information like the database credentials and the rows we want to show per page when we paginate results. helper.js is the home for any helper functions, like calculating offset for pagination.

The routes/programmingLanguages.js file will act as the glue between the URI and the corresponding function in the services/programmingLanguages.js service. The services folder will house all our services. One of them is db.js, which we use to talk with the MySQL database.

Another service is programmingLanguages.js, which will have methods like getMultiple, create, etc. to get and create the programming language resource. Basic mapping of the URI and the related service function will look like the code below:

GET /programming-languages → getMultiple()
POST /programming-languages → create()
PUT /programming-languages/:id → update()
DELETE /programming-languages/:id → remove()

Now, let’s code our GET programming languages API with pagination.

GET popular programming languages

To create our GET programming languages API, we’ll need to link our Node.js server with MySQL. To do so, we’ll use the mysql2 package from npm, which we can install with the npm i mysql2 command on the project root.

Next, we’ll create the config file on the root of the project with the following contents:

const config = {
  db: {
    /* don't expose password or any sensitive info, done only for demo */
    host: "db4free.net",
    user: "restapitest123",
    password: "restapitest123",
    database: "restapitest123",
  },
  listPerPage: 10,
};
module.exports = config;

Consequently, we’ll create the helper.js file with the code below:

function getOffset(currentPage = 1, listPerPage) {
  return (currentPage - 1) * [listPerPage];
}

function emptyOrRows(rows) {
  if (!rows) {
    return [];
  }
  return rows;
}

module.exports = {
  getOffset,
  emptyOrRows
}

For the fun part, we’ll add the route and link it to the services. First, we’ll connect to the database and enable running queries on the database in the services/db.js file:

const mysql = require('mysql2/promise');
const config = require('../config');

async function query(sql, params) {
  const connection = await mysql.createConnection(config.db);
  const [results, ] = await connection.execute(sql, params);

  return results;
}

module.exports = {
  query
}

Now, we’ll write up the services/programmingLanguage.js file that acts as the bridge between the route and the database:

const db = require('./db');
const helper = require('../helper');
const config = require('../config');

async function getMultiple(page = 1){
  const offset = helper.getOffset(page, config.listPerPage);
  const rows = await db.query(
    `SELECT id, name, released_year, githut_rank, pypl_rank, tiobe_rank 
    FROM programming_languages LIMIT ${offset},${config.listPerPage}`
  );
  const data = helper.emptyOrRows(rows);
  const meta = {page};

  return {
    data,
    meta
  }
}

module.exports = {
  getMultiple
}

After that, we’ll create the routes file in routes/programmingLanguages.js, which looks like the following:

const express = require('express');
const router = express.Router();
const programmingLanguages = require('../services/programmingLanguages');

/* GET programming languages. */
router.get('/', async function(req, res, next) {
  try {
    res.json(await programmingLanguages.getMultiple(req.query.page));
  } catch (err) {
    console.error(`Error while getting programming languages `, err.message);
    next(err);
  }
});

module.exports = router;

For the final piece of our GET endpoint, we need to wire up the route in the index.js file as follows:

const express = require("express");
const app = express();
const port = 3000;
const programmingLanguagesRouter = require("./routes/programmingLanguages");
app.use(express.json());
app.use(
  express.urlencoded({
    extended: true,
  })
);
app.get("/", (req, res) => {
  res.json({ message: "ok" });
});
app.use("/programming-languages", programmingLanguagesRouter);
/* Error handler middleware */
app.use((err, req, res, next) => {
  const statusCode = err.statusCode || 500;
  console.error(err.message, err.stack);
  res.status(statusCode).json({ message: err.message });
  return;
});
app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`);
});

We made two important changes in our entrypoint index.js file. For one, we added the code below:

const programmingLanguagesRouter = require('./routes/programmingLanguages');

Secondly, we link up the /programming-languages route to the router we just created as follows:

app.use('/programming-languages', programmingLanguagesRouter);

We’ve also added an error handler middleware to handle any errors and provide a proper status code and message.

After adding the GET endpoint, when we run our app again with node index.js and hit the browser with http://localhost:3000/programming-languages, we’ll see an output like the following:

Get Endpoint Node Index Output

Depending on the extensions you have installed on your browser, your output might look a little different.

Note that we’ve already implemented pagination for our GET API, which is possible because of the getOffset function in helper.js and the way we run the SELECT query in services/programmingLanguage.js. Try http://localhost:3000/programming-languages?page=2 to see languages 11–16.

POST a new programming language

Our POST API will allow us to create a new programming language in our table.

To create a POST programming language API in the /programming-languages endpoint, we’ll add code to the service and the routes files. In the service method, we’ll get the name, the release year, and other ranks from the request body, then insert them into the programming_languages table.

Append the following code to the services/programmingLanguages.js file:

async function create(programmingLanguage){
  const result = await db.query(
    `INSERT INTO programming_languages 
    (name, released_year, githut_rank, pypl_rank, tiobe_rank) 
    VALUES 
    (${programmingLanguage.name}, ${programmingLanguage.released_year}, ${programmingLanguage.githut_rank}, ${programmingLanguage.pypl_rank}, ${programmingLanguage.tiobe_rank})`
  );

  let message = 'Error in creating programming language';

  if (result.affectedRows) {
    message = 'Programming language created successfully';
  }

  return {message};
}

Make sure you export the following function as well:

module.exports = {
  getMultiple,
  create
}

For the function above to be accessible, we need to add a route to link it up in the routes/programmingLanguages.js file as follows:

/* POST programming language */
router.post('/', async function(req, res, next) {
  try {
    res.json(await programmingLanguages.create(req.body));
  } catch (err) {
    console.error(`Error while creating programming language`, err.message);
    next(err);
  }
});

PUT to update an existing programming language

To update an existing programming language, we’ll use the /programming-languages/:id endpoint, where we’ll get the data to update the language. To update a programming language, we’ll run the UPDATE query based on the data we got in the request.

PUT is an idempotent action, meaning if the same call is made over and over again, it will produce the exact same results. To enable updating existing records, we’ll add the following code to the programming language service:

async function update(id, programmingLanguage){
  const result = await db.query(
    `UPDATE programming_languages 
    SET name="${programmingLanguage.name}", released_year=${programmingLanguage.released_year}, githut_rank=${programmingLanguage.githut_rank}, 
    pypl_rank=${programmingLanguage.pypl_rank}, tiobe_rank=${programmingLanguage.tiobe_rank} 
    WHERE id=${id}` 
  );

  let message = 'Error in updating programming language';

  if (result.affectedRows) {
    message = 'Programming language updated successfully';
  }

  return {message};
}

Make sure you export this function as well as we did before:

module.exports = {
  getMultiple,
  create,
  update,
};

To wire up the code with the PUT endpoint, we’ll add the code below to the programming languages route file, just above module.exports = router;:

/* PUT programming language */
router.put('/:id', async function(req, res, next) {
  try {
    res.json(await programmingLanguages.update(req.params.id, req.body));
  } catch (err) {
    console.error(`Error while updating programming language`, err.message);
    next(err);
  }
});

Now, we have the ability to update any existing programming language. For instance, we can update a language’s name if we see a typo.

DELETE a programming language

We’ll use the /programming-languages/:id path with the HTTP DELETE method to add the functionality to delete a programming language. Go ahead and run the code below:

async function remove(id){
  const result = await db.query(
    `DELETE FROM programming_languages WHERE id=${id}`
  );

  let message = 'Error in deleting programming language';

  if (result.affectedRows) {
    message = 'Programming language deleted successfully';
  }

  return {message};
}

Don’t forget to export this function as well. Once again, to link up the service with the route, we’ll add the following code to the routes/programmingLanguages.js file:

/* DELETE programming language */
router.delete('/:id', async function(req, res, next) {
  try {
    res.json(await programmingLanguages.remove(req.params.id));
  } catch (err) {
    console.error(`Error while deleting programming language`, err.message);
    next(err);
  }
});

Testing our APIs

After you have the Node.js Express server running with node index.js, you can test all the API endpoints. To create a new programming language, let’s go with Dart, run the following cURLcommand. Alternately, you can use Postman or any other HTTP client:

curl -i -X POST -H 'Accept: application/json' \
    -H 'Content-type: application/json' http://localhost:3000/programming-languages \
    --data '{"name":"dart", "released_year": 2011, "githut_rank": 13, "pypl_rank": 20, "tiobe_rank": 25}'

The code above will result in the following output:

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 55
ETag: W/"37-3mETlnRrtfrms6wlAjdgAXKq9GE"
Date: Mon, 01 Feb 2021 11:20:07 GMT
Connection: keep-alive

{"message":"Programming language created successfully"}

You can remove the X-Powered-By header and add other security response headers using Express.js Helmet, which will be a great addition to improve the API’s security. For now, let’s update the GitHut rank of Dart from 13 to 12:

curl -i -X PUT -H 'Accept: application/json' \
    -H 'Content-type: application/json' http://localhost:3000/programming-languages/17 \
    --data '{"name":"dart", "released_year": 2011, "githut_rank": 12, "pypl_rank": 20, "tiobe_rank": 25}'

The code above will generate an output like below:

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 55
ETag: W/"37-0QPAQsRHsm23S9CNV3rPa+AFuXo"
Date: Mon, 01 Feb 2021 11:40:03 GMT
Connection: keep-alive

{"message":"Programming language updated successfully"}

To test out the DELETE API, you can use the following cURL to delete Dart with ID 17:

curl -i -X DELETE -H 'Accept: application/json' \
    -H 'Content-type: application/json' http://localhost:3000/programming-languages/17

The code above will result in the following output:

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 55
ETag: W/"37-aMzd+8NpWQ09igvHbNLorsXxGFo"
Date: Mon, 01 Feb 2021 11:50:17 GMT
Connection: keep-alive

{"message":"Programming language deleted successfully"}

If you’re more used to a visual interface for testing, for instance, Postman, you can import the cURL commands into Postman.

Further considerations

For the sake of simplicity in this tutorial, we kept our example fairly simple. However, if this was a real-life API, and not a demo, I’d highly recommend the following:

  • Use a robust validation library like Joi to precisely validate the input, for example, to ensure the name of the programming language is required and doesn’t already exist in the database.
  • Improve security by adding Helmet.js to Express.js
  • Streamline logs in a more manageable way using a Node.js logging library like Winston
  • Use Docker for the Node.js application

Conclusion

We now have a functioning API server that uses Node.js and MySQL. In this tutorial, we learned how to set up MySQL on a free service. We then created an Express.js server that can handle various HTTP methods in connection to how it translates to SQL queries.

The example REST API in this tutorial serves as a good starting point and foundation for building real-world, production-ready REST APIs, wherein you can practice the additional considerations describes above. I hope you enjoyed this article, happy coding!

Original article source at https://blog.logrocket.com

#node #mysql #api #expressjs #database #restapi 

How to Build a REST API with Node, Express & MySQL Database
Jacob Banks

Jacob Banks

1664849088

.NET vs Ruby on Rails vs. Node.js: 3 Frameworks / 3 APIs

In this tutorial we compare 3 of the most popular API development frameworks: Ruby on Rails, Node (with Express) and .NET, and build an API with each.

⏲️ Time Codes ⏲️

Introduction & Theory
- 3:30 - Overview
- 9:17 - Ingredients
- 12:56 - Environement Set Up
- 18:46 - WSL Windows Subsystem for Linux
- 22:48 - VSCode Extension

Ruby on Rails API
- 25:23 - Pre-flight checks
- 27:46 - Install Rails
- 31:42 - Scaffold Rails API
- 38:59 - Run and Test API

Node JS
- 43:58 - Pre-flight checks
- 47:48 - Scaffold Node API
- 53:18 - Express and a simple endpoint
- 1:00:22 - Data access
- 1:11:08 - GET endpoint
- 1:17:51 - Nodemon and Bodyparser
- 1:22:18 - POST endpoint

.NET
- 1:35:40 - Pre-flight checks
- 1:41:27 - Install packages and remove demo code
- 1:48:06 - Adding our model
- 1:53:28 - DBContext / Data access
- 1:58:34 - Register DB Context
- 2:04:11 - Database migrations
- 2:07:46 - GET endpoint
- 2:12:59 - POST endpoint

Credits
- 2:19:27

💾 GitHub Repo: https://github.com/binarythistle/S05E05---3APIs
🔗 Install WSL: https://learn.microsoft.com/en-us/windows/wsl/install?WT.mc_id=DX-MVP-5004571 
🔗 Install SQL Lite Ubuntu: https://www.digitalocean.com/community/tutorials/how-to-install-and-use-sqlite-on-ubuntu-20-04 
🔗 Dotnet Core Framework: https://dotnet.microsoft.com/en-us/download?WT.mc_id=DX-MVP-5004571 
🔗 NVM Install: https://github.com/nvm-sh/nvm 
🔗 Install Ruby-Install: https://github.com/postmodern/ruby-install#install 
🔗 Install chruby: https://github.com/postmodern/chruby#install 
 

#dotnet #node #nodejs #expressjs #rubyonrails #api 

.NET vs Ruby on Rails vs. Node.js: 3 Frameworks / 3 APIs
Charles Cooper

Charles Cooper

1664357121

MERN Stack Chat App with Socket.IO

MERN Stack with Socket.io Chat App for Beginners

MERN Stack Socket.io chat application for beginners with project include MERN = Nodejs with Expressjs, React with React Router, Mongodb with Mongoose and socket.io for websocket thing

🌟 Course Contents 🌟
⏳ (00:00:00) Intro to course
⏳ (00:04:02) Course Outline
⏳ (00:07:43) Setup Nodejs
⏳ (00:13:16) First socket connection
⏳ (00:20:35) React with socket.io
⏳ (00:30:33) Send and receive message
⏳ (00:42:18) Make Chat Working
⏳ (00:51:17) Chat box design
⏳ (01:00:00) Message box design
⏳ (01:09:43) Typing event
⏳ (01:22:34) React router
⏳ (01:38:24) Join room
⏳ (01:47:26) Room chat
⏳ (01:57:53) Best practice Clean Code
⏳ (02:08:48) Dynamic room
⏳ (02:20:41) Rooms on MongoDB
⏳ (02:37:19) Dummy user
⏳ (02:45:25) Remove room
⏳ (03:00:07) File sharing

Source Code: https://github.com/bitfumes/mern-stack-socket-chat

#mern #socketio #mongodb #expressjs #node #react 

MERN Stack Chat App with Socket.IO
Dylan  Iqbal

Dylan Iqbal

1663816330

MERN Stack Tutorial for Beginners with Full Project

MERN Stack complete course with project include MERN = Nodejs with Expressjs, React with Redux, Mongodb with Mongoose and authentication with passportjs jwt library

What is the MERN stack?

The phrase MERN stack refers to the following technologies:

  • MongoDB, a cross-platform document-oriented database program
  • Express.js, a web application framework for Node.js
  • React, a JavaScript library for building user interfaces
  • Node.js, an open-source, cross-platform JavaScript run-time environment that executes JavaScript code outside of a browser

🌟 Course Contents 🌟
⏳ (00:00:00) Intro to course
⏳ (00:06:38) Course Outline
⏳ (00:11:38) Setup MERN project
⏳ (00:17:38) Setup MongoDB atlas
⏳ (00:25:20) Transaction form
⏳ (00:35:54) Handle CORS & Nodemon
⏳ (00:42:12) API body parser
⏳ (00:47:37) First transaction
⏳ (00:55:24) Fetch all transactions
⏳ (01:02:56) Express routes
⏳ (01:10:58) use material UI
⏳ (01:18:59) Design form 
⏳ (01:25:58) Submit form
⏳ (01:31:33) Transactions table
⏳ (01:40:31) Delete transactions
⏳ (01:52:47) Update design
⏳ (02:02:55) Update API
⏳ (02:09:30) React router dom
⏳ (02:21:53) Register user part 1
⏳ (02:34:10) Register user part 2
⏳ (02:45:50) Login user part 1
⏳ (02:54:18) Login user part 2
⏳ (03:00:14) Passport JWT authenticate
⏳ (03:13:22) DotEnv 
⏳ (03:23:23) Top level component middleware
⏳ (03:34:44) Validate token
⏳ (03:43:36) React redux store
⏳ (03:53:46) Auth store
⏳ (04:06:14) Express controllers
⏳ (04:14:40) Transactions of user
⏳ (04:43:55) Category to transaction
⏳ (04:39:07) Show all category
⏳ (04:46:59) Delete category
⏳ (04:57:12) Update & create category
⏳ (05:10:52) Monthly expense chart
⏳ (05:24:36) Deploy MERN stack to Heroku and Netlify

Source Code: https://github.com/bitfumes/mern-stack-2022


The MERN stack: A Complete Tutorial for Beginners

This tutorial is all about the MERN stack. We’ll outline the basics of the MERN stack and demonstrate how to use it by developing a simple CRUD application from scratch.

Server setup with Express.js and Node.js

To begin our MERN stack tutorial, we’ll show you how to set up a server with Express.js and Node.js.

npm package initialization

To create a project folder, enter the folder through the terminal, then run the following command:

$ npm init

Now it will ask you some questions about package name, version, entry point, etc. Hit enter if you want to keep the default. After that, you will get something like this:

Creating Our package.json File

Select yes and you’re ready to go. It creates a file named package.json.

Installing dependencies

Now, I would like to add some dependencies:

$ npm i express mongoose body-parser bcryptjs validation

Type or copy the command above and hit the enter button. You’ll see something like this:

Adding Project File Dependencies

  • bcryptjs is a password hashing function designed by Niels Provos and David Mazières
  • body-parser allows us to get the data throughout the request
  • express is our main framework
  • mongoose is used to connect/interact with MongoDB
  • validation (as its name implies) is used for validation

Now I want to add nodemon as a dev dependency. If you don’t want to add this, you can skip it — it’s optional.

$ npm i -D nodemon

nodemon is a utility that will monitor for any changes in your source and automatically restart your server.

At that point, your package.json should look like this:

package.json File With Dependencies

Setting the entry point

Now create a file named app.js for our entry point. You can create this from the project folder with the command below (on Mac):

$ touch app.js

Then paste the code below:

// app.js

const express = require('express');

const app = express();

app.get('/', (req, res) => res.send('Hello world!'));

const port = process.env.PORT || 8082;

app.listen(port, () => console.log(`Server running on port ${port}`));

Now, run the command

$ node app

You will see Server running on port 8082. You can also check it from the browser: open the browser and enter http://localhost:8082.

At this point, if we change anything, we need to restart the server manually. But if we set up nodemon, then we don’t have to restart it every time; nodemon will watch if there is any change and restart the server automatically.

So what you need to do for that is a little change to the scripts in our package.json file. See below:

// package.json

{
  "name": "mern_a_to_z",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "start": "node app.js",
    "app": "nodemon app.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/nurislam03/MERN_A_to_Z.git"
  },
  "author": "Nur Islam",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/nurislam03/MERN_A_to_Z/issues"
  },
  "homepage": "https://github.com/nurislam03/MERN_A_to_Z#readme",
  "dependencies": {
    "bcryptjs": "^2.4.3",
    "body-parser": "^1.19.0",
    "express": "^4.17.1",
    "mongoose": "^5.5.15",
    "validation": "0.0.1"
  },
  "devDependencies": {
    "nodemon": "^1.19.1"
  }
}

So, now you can run your project using this command:

$ npm run app

If you get any error at this point, then run the commands below:

$ npm install
$ npm run app

You will see the following changes in your terminal if everything goes right:

Running Project Successfully

Database management with MongoDB

Now it’s time to work on our MERN database setup with MongoDB. For simplicity, we will use MongoDB Atlas.

Creating an account for MongoDB Atlas

MongoDB Atlas is a fully managed cloud database developed by the same team that built MongoDB.

First, you need an account. Create one and follow the procedure. After creating an account, you will see something like this:

MongoDB Atlas Homescreen

Click on the Project 0 section (top left) and you will see a button for creating a new project. Create a project and select the project.

Now, click on the Build a Cluster button from the project you have created. It will show you all the information. At the bottom, you will see a section called Cluster Name, click on that and enter a name for the database, then hit the Create Cluster button.

After two to three minutes, if everything goes well, you will find something like this:

Creating A Cluster In MongoDB Atlas

Click on the CONNECT button and fill in the username and password form for your database.

Setting Up Our Connection

Now hit the Create MongoDB User button. You can also choose either your current IP address or a different IP address, it’s up to you.

Now, if you follow the CONNECT button or the Choose a connection method button, you will see some different methods. Select accordingly.

Connection Methods Options

In this case, select the Connect Your Application section.

Now you will get your database link, which we will use in our next step.

Connection String Output

Our database is ready — now we need to add it to our project.

Inside the project folder, create another folder named config and inside it create two files named default.json and db.js. Add the following code:

// default.json

{
  "mongoURI":
    "mongodb+srv://mern123:<password>@mernatoz-9kdpd.mongodb.net/test?retryWrites=true&w=majority"
}
 /* Replace <password> with your database password */

/* ------------------------------------------------------------------ */
// db.js

const mongoose = require('mongoose');
const config = require('config');
const db = config.get('mongoURI');

const connectDB = async () => {
  try {
    await mongoose.connect(
      db,
      {
        useNewUrlParser: true
      }
    );

    console.log('MongoDB is Connected...');
  } catch (err) {
    console.error(err.message);
    process.exit(1);
  }
};

module.exports = connectDB;

NOTE: We need a little change in our app.js file to connect to the database. Update your app.js with this:

// app.js

const express = require('express');
const connectDB = require('./config/db');

const app = express();

// Connect Database
connectDB();

app.get('/', (req, res) => res.send('Hello world!'));

const port = process.env.PORT || 8082;

app.listen(port, () => console.log(`Server running on port ${port}`));

We need another dependency package called config for the global variable to run our project. Use the following command to install it to the project:

$ npm i config

Now, you can run the project using the following command:

$ npm run app
Successfully Connected Server

Great! So far we are on the right track. Our database is successfully connected. Now time to complete the route setup, and after that, we will see how to create RESTful APIs.

Building RESTful APIs with the MERN stack

Create a folder named routes. In it, create another folder named api, which will hold all our APIs.

Inside the api folder, create a file named books.js. We will create some APIs here to show how it works in a moment.

Now update your books.js with the following code:

// routes/api/books.js

const express = require('express');
const router = express.Router();

// Load Book model
const Book = require('../../models/Book');

// @route GET api/books/test
// @description tests books route
// @access Public
router.get('/test', (req, res) => res.send('book route testing!'));

// @route GET api/books
// @description Get all books
// @access Public
router.get('/', (req, res) => {
  Book.find()
    .then(books => res.json(books))
    .catch(err => res.status(404).json({ nobooksfound: 'No Books found' }));
});

// @route GET api/books/:id
// @description Get single book by id
// @access Public
router.get('/:id', (req, res) => {
  Book.findById(req.params.id)
    .then(book => res.json(book))
    .catch(err => res.status(404).json({ nobookfound: 'No Book found' }));
});

// @route GET api/books
// @description add/save book
// @access Public
router.post('/', (req, res) => {
  Book.create(req.body)
    .then(book => res.json({ msg: 'Book added successfully' }))
    .catch(err => res.status(400).json({ error: 'Unable to add this book' }));
});

// @route GET api/books/:id
// @description Update book
// @access Public
router.put('/:id', (req, res) => {
  Book.findByIdAndUpdate(req.params.id, req.body)
    .then(book => res.json({ msg: 'Updated successfully' }))
    .catch(err =>
      res.status(400).json({ error: 'Unable to update the Database' })
    );
});

// @route GET api/books/:id
// @description Delete book by id
// @access Public
router.delete('/:id', (req, res) => {
  Book.findByIdAndRemove(req.params.id, req.body)
    .then(book => res.json({ mgs: 'Book entry deleted successfully' }))
    .catch(err => res.status(404).json({ error: 'No such a book' }));
});

module.exports = router;

Database model

In order to interact with our database, we need to create a model for each of our resources. So, create a folder called models in the root, and inside the models folder, create a file called Book.js and update it with this:

// models/Book.js

const mongoose = require('mongoose');

const BookSchema = new mongoose.Schema({
  title: {
    type: String,
    required: true
  },
  isbn: {
    type: String,
    required: true
  },
  author: {
    type: String,
    required: true
  },
  description: {
    type: String
  },
  published_date: {
    type: Date
  },
  publisher: {
    type: String
  },
  updated_date: {
    type: Date,
    default: Date.now
  }
});

module.exports = Book = mongoose.model('book', BookSchema);

Run the project to see if everything is fine at this point, and you can test all the APIs through Postman (note that before testing APIs using Postman, you need to run the project first). You can download Postman here.

Building the frontend

So far, so good! Now that we’ve set up our backend, it’s time to transition to the frontend part of this MERN stack tutorial.

In this section, we’ll use React to build our user interfaces. React is a JavaScript library for building user interfaces. It is maintained by Facebook and a community of individual developers and other companies.

We’ll use Create React App to generate our initial file setup. CRA is a comfortable environment for learning React and is the best way to start building applications in React. It offers a modern build setup with no configuration.

We’ll also use webpack and Babel to bundle our modules and compile our JavaScript, respectively. If you don’t know webpack or Babel well, no problem; you don’t need to install or configure tools like webpack or Babel. They’re preconfigured and hidden so that you can focus on the code. Just create a project, and you’re good to go.

You’ll also need any version of Node.js greater than 8.10 and any version of npm greater than 5.6 installed on your local development machine.

Setting up Create React App

Set any directory using a terminal where you want to keep all the files of this project and run the following command to get the initial setup file:

 $ npx create-react-app my-app

You can replace my-app with whatever you’d like to use as your project name. For example, my project name is mern_a_to_z_client, and my command is:

 $ npx create-react-app mern_a_to_z_client

Note: The project name must be in lowercase letters.

If everything goes right, then you will see something like the following image, where you will find some instructions along with the commands.

Project Successfully Created In Create React App

Before using any built-in command, we need to go inside the project folder.

 $ cd mern_a_to_z_client

Now that we are in the project directory, we can use those available commands. If you’re using Yarn:

$ yarn start

Or, if using npm:

$ npm start

To run the app in development mode, you can use any of the above commands, and you will see the following message in your terminal.

Running App In Development Mode

Now open http://localhost:3000 to view it in the browser. This page will automatically reload if you make changes to the code.

Viewing Our App In The Browser

Initial project structure

Inside the project directory, our initial file structure should look like this:

Our Initial Project Structure

Adding Bootstrap and Font Awesome to your React app

We have got our initial setup file for the front-end part. Now we can start integrating our back end with our front end. Before that, though, I want to add Bootstrap and Font Awesome’s CDN to our project.

Open the file called index.html, which is in the public folder mern_a_to_z_client/public/index.html, and replace everything with the following code:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->

    <!-- bootstrap css cdn -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

    <!-- fontawesome cdn -->
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css" integrity="sha384-hWVjflwFxL6sNzntih27bfxkr27PmbbK/iSvJ+a4+0owXq79v+lsFkW54bOGbiDQ" crossorigin="anonymous">

    <title>MERN A to Z</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->

    <!-- bootstrap JS cdn -->
    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>

  </body>
</html>

Frontend tasks and features

We will work with five different features:

  1. Add, create or save a new book
  2. Show all the books we have stored in the database
  3. Show a single book
  4. Update a book
  5. Delete a book

Dependencies packages installation

Now, use the following command to add some necessary dependencies:

$ npm install --save react-router-dom
$ npm install --save axios

Why Axios?

Axios is a lightweight HTTP client based similar to a Fetch API. Axios is a promise-based async/await library for readable asynchronous code. We can easily integrate with React, and it is effortless to use in any front-end framework.

We’ll call our APIs through Axios.

Package.json file

At this point, our package.json file should be similar to this; versions can be similar or different:

// MERN_A_to_Z_Client - package.json

{
  "name": "mern_a_to_z_client",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "axios": "^0.19.0",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-router-dom": "^5.0.1",
    "react-scripts": "3.0.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

Creating the component file

Inside the src folder (mern_a_to_z_client/src/), create another folder called components, and inside it, create five different files:

  1. CreateBook.js
  2. ShowBookList.js
  3. BookCard.js
  4. ShowBookDetails.js
  5. UpdateBookInfo.js

We will work with these five files a bit later.

Setup route

Open the folder called App.js inside the src folder (mern_a_to_z_client/src/App.js), and replace it with the following code:

import React, { Component } from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import './App.css';

import CreateBook from './components/CreateBook';
import ShowBookList from './components/ShowBookList';
import ShowBookDetails from './components/ShowBookDetails';
import UpdateBookInfo from './components/UpdateBookInfo';

class App extends Component {
  render() {
    return (
      <Router>
        <div>
          <Route exact path='/' component={ShowBookList} />
          <Route path='/create-book' component={CreateBook} />
          <Route path='/edit-book/:id' component={UpdateBookInfo} />
          <Route path='/show-book/:id' component={ShowBookDetails} />
        </div>
      </Router>
    );
  }
}

export default App;

Here, we define all the routes. For a specific path definition, its corresponding component will be rendered. We have not implemented these files/components yet — just completed the path setup.

Updating the CSS file

Update a CSS file called App.css in the src folder with the following code:

.App {
  text-align: center;
}

.App-logo {
  animation: App-logo-spin infinite 20s linear;
  height: 40vmin;
  pointer-events: none;
}

.App-header {
  background-color: #282c34;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: calc(10px + 2vmin);
  color: white;
}

.App-link {
  color: #61dafb;
}

@keyframes App-logo-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

.CreateBook {
  background-color: #2c3e50;
  min-height: 100vh;
  color: white;
}

.ShowBookDetails {
  background-color: #2c3e50;
  min-height: 100vh;
  color: white;
}

.UpdateBookInfo {
  background-color: #2c3e50;
  min-height: 100vh;
  color: white;
}

.ShowBookList {
  background-color: #2c3e50;
  height: 100%;
  width: 100%;
  min-height: 100vh;
  min-width: 100px;
  color: white;
}


/* BookList Styles */
.list {
  display: grid;
  margin: 20px 0 50px 0;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 1fr;
  grid-gap: 2em;
}

.card-container {
  width: 250px;
  border: 1px solid rgba(0,0,.125);
  margin: 0 auto;
  border-radius: 5px;
  overflow: hidden;
}

.desc {
  height: 130px;
  padding: 10px;
}

.desc h2 {
  font-size: 1em;
  font-weight: 400;
}

.desc h3, p {
  font-weight: 300;
}

.desc h3 {
  color: #6c757d;
  font-size: 1em;
  padding: 10px 0 10px 0;
}

Adding our feature components

Now it’s time to add feature components to our MERN stack project.

Create a new book

Our CreateBook.js file is responsible for adding, creating, or saving a new book or a book’s info. So, update CreateBook.js with the following code:

import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import '../App.css';
import axios from 'axios';


class CreateBook extends Component {
  constructor() {
    super();
    this.state = {
      title: '',
      isbn:'',
      author:'',
      description:'',
      published_date:'',
      publisher:''
    };
  }

  onChange = e => {
    this.setState({ [e.target.name]: e.target.value });
  };

  onSubmit = e => {
    e.preventDefault();

    const data = {
      title: this.state.title,
      isbn: this.state.isbn,
      author: this.state.author,
      description: this.state.description,
      published_date: this.state.published_date,
      publisher: this.state.publisher
    };

    axios
      .post('http://localhost:8082/api/books', data)
      .then(res => {
        this.setState({
          title: '',
          isbn:'',
          author:'',
          description:'',
          published_date:'',
          publisher:''
        })
        this.props.history.push('/');
      })
      .catch(err => {
        console.log("Error in CreateBook!");
      })
  };

  render() {
    return (
      <div className="CreateBook">
        <div className="container">
          <div className="row">
            <div className="col-md-8 m-auto">
              <br />
              <Link to="/" className="btn btn-outline-warning float-left">
                  Show BooK List
              </Link>
            </div>
            <div className="col-md-8 m-auto">
              <h1 className="display-4 text-center">Add Book</h1>
              <p className="lead text-center">
                  Create new book
              </p>

              <form noValidate onSubmit={this.onSubmit}>
                <div className='form-group'>
                  <input
                    type='text'
                    placeholder='Title of the Book'
                    name='title'
                    className='form-control'
                    value={this.state.title}
                    onChange={this.onChange}
                  />
                </div>
                <br />

                <div className='form-group'>
                  <input
                    type='text'
                    placeholder='ISBN'
                    name='isbn'
                    className='form-control'
                    value={this.state.isbn}
                    onChange={this.onChange}
                  />
                </div>

                <div className='form-group'>
                  <input
                    type='text'
                    placeholder='Author'
                    name='author'
                    className='form-control'
                    value={this.state.author}
                    onChange={this.onChange}
                  />
                </div>

                <div className='form-group'>
                  <input
                    type='text'
                    placeholder='Describe this book'
                    name='description'
                    className='form-control'
                    value={this.state.description}
                    onChange={this.onChange}
                  />
                </div>

                <div className='form-group'>
                  <input
                    type='date'
                    placeholder='published_date'
                    name='published_date'
                    className='form-control'
                    value={this.state.published_date}
                    onChange={this.onChange}
                  />
                </div>
                <div className='form-group'>
                  <input
                    type='text'
                    placeholder='Publisher of this Book'
                    name='publisher'
                    className='form-control'
                    value={this.state.publisher}
                    onChange={this.onChange}
                  />
                </div>

                <input
                    type="submit"
                    className="btn btn-outline-warning btn-block mt-4"
                />
              </form>
          </div>
          </div>
        </div>
      </div>
    );
  }
}

export default CreateBook;

Show all books

The ShowBookList.js component will be responsible for showing all the books we already have stored in our database. Update ShowBookList.js with this code:

import React, { Component } from 'react';
import '../App.css';
import axios from 'axios';
import { Link } from 'react-router-dom';
import BookCard from './BookCard';

class ShowBookList extends Component {
  constructor(props) {
    super(props);
    this.state = {
      books: []
    };
  }

  componentDidMount() {
    axios
      .get('http://localhost:8082/api/books')
      .then(res => {
        this.setState({
          books: res.data
        })
      })
      .catch(err =>{
        console.log('Error from ShowBookList');
      })
  };


  render() {
    const books = this.state.books;
    console.log("PrintBook: " + books);
    let bookList;

    if(!books) {
      bookList = "there is no book record!";
    } else {
      bookList = books.map((book, k) =>
        <BookCard book={book} key={k} />
      );
    }

    return (
      <div className="ShowBookList">
        <div className="container">
          <div className="row">
            <div className="col-md-12">
              <br />
              <h2 className="display-4 text-center">Books List</h2>
            </div>

            <div className="col-md-11">
              <Link to="/create-book" className="btn btn-outline-warning float-right">
                + Add New Book
              </Link>
              <br />
              <br />
              <hr />
            </div>

          </div>

          <div className="list">
                {bookList}
          </div>
        </div>
      </div>
    );
  }
}

export default ShowBookList;

Creating a card for each book

Here we use a functional component called BookCard.js, which takes a book’s info from ShowBookList.js and makes a card for each book. Write the following code to update your BookCard.js file:

import React from 'react';
import { Link } from 'react-router-dom';
import '../App.css';

const BookCard = (props) => {
    const  book  = props.book;

    return(
        <div className="card-container">
            <img src="https://commapress.co.uk/books/the-book-of-cairo/cairo-provisional-v3/image%2Fspan3" alt="" />
            <div className="desc">
                <h2>
                    <Link to={`/show-book/${book._id}`}>
                        { book.title }
                    </Link>
                </h2>
                <h3>{book.author}</h3>
                <p>{book.description}</p>
            </div>
        </div>
    )
};

export default BookCard;

NOTE: Here, I used the same img src for each book, since each book’s respective image may not always be available. Change the image source, and you can also use a different image for each book.

Show a book’s info

The ShowBookDetails component has one task: it shows all the info we have about any book. We have both delete and edit buttons here to get access.

import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import '../App.css';
import axios from 'axios';

class showBookDetails extends Component {
  constructor(props) {
    super(props);
    this.state = {
      book: {}
    };
  }

  componentDidMount() {
    // console.log("Print id: " + this.props.match.params.id);
    axios
      .get('http://localhost:8082/api/books/'+this.props.match.params.id)
      .then(res => {
        // console.log("Print-showBookDetails-API-response: " + res.data);
        this.setState({
          book: res.data
        })
      })
      .catch(err => {
        console.log("Error from ShowBookDetails");
      })
  };

  onDeleteClick (id) {
    axios
      .delete('http://localhost:8082/api/books/'+id)
      .then(res => {
        this.props.history.push("/");
      })
      .catch(err => {
        console.log("Error form ShowBookDetails_deleteClick");
      })
  };


  render() {

    const book = this.state.book;
    let BookItem = <div>
      <table className="table table-hover table-dark">
        {/* <thead>
          <tr>
            <th scope="col">#</th>
            <th scope="col">First</th>
            <th scope="col">Last</th>
            <th scope="col">Handle</th>
          </tr>
        </thead> */}
        <tbody>
          <tr>
            <th scope="row">1</th>
            <td>Title</td>
            <td>{ book.title }</td>
          </tr>
          <tr>
            <th scope="row">2</th>
            <td>Author</td>
            <td>{ book.author }</td>
          </tr>
          <tr>
            <th scope="row">3</th>
            <td>ISBN</td>
            <td>{ book.isbn }</td>
          </tr>
          <tr>
            <th scope="row">4</th>
            <td>Publisher</td>
            <td>{ book.publisher }</td>
          </tr>
          <tr>
            <th scope="row">5</th>
            <td>Published Date</td>
            <td>{ book.published_date }</td>
          </tr>
          <tr>
            <th scope="row">6</th>
            <td>Description</td>
            <td>{ book.description }</td>
          </tr>
        </tbody>
      </table>
    </div>

    return (
      <div className="ShowBookDetails">
        <div className="container">
          <div className="row">
            <div className="col-md-10 m-auto">
              <br /> <br />
              <Link to="/" className="btn btn-outline-warning float-left">
                  Show Book List
              </Link>
            </div>
            <br />
            <div className="col-md-8 m-auto">
              <h1 className="display-4 text-center">Book's Record</h1>
              <p className="lead text-center">
                  View Book's Info
              </p>
              <hr /> <br />
            </div>
          </div>
          <div>
            { BookItem }
          </div>

          <div className="row">
            <div className="col-md-6">
              <button type="button" className="btn btn-outline-danger btn-lg btn-block" onClick={this.onDeleteClick.bind(this,book._id)}>Delete Book</button><br />
            </div>

            <div className="col-md-6">
              <Link to={`/edit-book/${book._id}`} className="btn btn-outline-info btn-lg btn-block">
                    Edit Book
              </Link>
              <br />
            </div>

          </div>
            {/* <br />
            <button type="button" class="btn btn-outline-info btn-lg btn-block">Edit Book</button>
            <button type="button" class="btn btn-outline-danger btn-lg btn-block">Delete Book</button> */}

        </div>
      </div>
    );
  }
}

export default showBookDetails;

Update a book’s info

UpdateBookInfo.js, as its name indicates, is responsible for updating a book’s info. An Edit Book button will trigger this component to perform. After clicking Edit Book, we will see a form with the old info, which we will be able to edit or replace.

import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import axios from 'axios';
import '../App.css';

class UpdateBookInfo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      title: '',
      isbn: '',
      author: '',
      description: '',
      published_date: '',
      publisher: ''
    };
  }

  componentDidMount() {
    // console.log("Print id: " + this.props.match.params.id);
    axios
      .get('http://localhost:8082/api/books/'+this.props.match.params.id)
      .then(res => {
        // this.setState({...this.state, book: res.data})
        this.setState({
          title: res.data.title,
          isbn: res.data.isbn,
          author: res.data.author,
          description: res.data.description,
          published_date: res.data.published_date,
          publisher: res.data.publisher
        })
      })
      .catch(err => {
        console.log("Error from UpdateBookInfo");
      })
  };

  onChange = e => {
    this.setState({ [e.target.name]: e.target.value });
  };

  onSubmit = e => {
    e.preventDefault();

    const data = {
      title: this.state.title,
      isbn: this.state.isbn,
      author: this.state.author,
      description: this.state.description,
      published_date: this.state.published_date,
      publisher: this.state.publisher
    };

    axios
      .put('http://localhost:8082/api/books/'+this.props.match.params.id, data)
      .then(res => {
        this.props.history.push('/show-book/'+this.props.match.params.id);
      })
      .catch(err => {
        console.log("Error in UpdateBookInfo!");
      })
  };


  render() {
    return (
      <div className="UpdateBookInfo">
        <div className="container">
          <div className="row">
            <div className="col-md-8 m-auto">
              <br />
              <Link to="/" className="btn btn-outline-warning float-left">
                  Show BooK List
              </Link>
            </div>
            <div className="col-md-8 m-auto">
              <h1 className="display-4 text-center">Edit Book</h1>
              <p className="lead text-center">
                  Update Book's Info
              </p>
            </div>
          </div>

          <div className="col-md-8 m-auto">
          <form noValidate onSubmit={this.onSubmit}>
            <div className='form-group'>
              <label htmlFor="title">Title</label>
              <input
                type='text'
                placeholder='Title of the Book'
                name='title'
                className='form-control'
                value={this.state.title}
                onChange={this.onChange}
              />
            </div>
            <br />

            <div className='form-group'>
            <label htmlFor="isbn">ISBN</label>
              <input
                type='text'
                placeholder='ISBN'
                name='isbn'
                className='form-control'
                value={this.state.isbn}
                onChange={this.onChange}
              />
            </div>

            <div className='form-group'>
            <label htmlFor="author">Author</label>
              <input
                type='text'
                placeholder='Author'
                name='author'
                className='form-control'
                value={this.state.author}
                onChange={this.onChange}
              />
            </div>

            <div className='form-group'>
            <label htmlFor="description">Description</label>
              <input
                type='text'
                placeholder='Describe this book'
                name='description'
                className='form-control'
                value={this.state.description}
                onChange={this.onChange}
              />
            </div>

            <div className='form-group'>
            <label htmlFor="published_date">Published Date</label>
              <input
                type='date'
                placeholder='published_date'
                name='published_date'
                className='form-control'
                value={this.state.published_date}
                onChange={this.onChange}
              />
            </div>
            <div className='form-group'>
            <label htmlFor="publisher">Publisher</label>
              <input
                type='text'
                placeholder='Publisher of this Book'
                name='publisher'
                className='form-control'
                value={this.state.publisher}
                onChange={this.onChange}
              />
            </div>

            <button type="submit" className="btn btn-outline-info btn-lg btn-block">Update Book</button>
            </form>
          </div>

        </div>
      </div>
    );
  }
}

export default UpdateBookInfo;

Connecting the frontend to the backend

We just implemented all of our components! Now we need a little change in our server-side (back-end) project.

Changes required on the backend

If we try to call our back-end API from the front-end part, it gets an error that says: “Access to XMLHttpRequest at ‘http://localhost:8082/api/books&#8217; from origin ‘http://localhost:3000&#8217; has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.”

To solve this, we need to install cors in our back-end (server-side) project. Go to the project folder (e.g., MERN_A_to_Z) and run:

$ npm install cors

Now, update app.js (the back end’s entry point) with the following code:

// app.js

const express = require('express');
const connectDB = require('./config/db');
var cors = require('cors');

// routes
const books = require('./routes/api/books');

const app = express();

// Connect Database
connectDB();

// cors
app.use(cors({ origin: true, credentials: true }));

// Init Middleware
app.use(express.json({ extended: false }));

app.get('/', (req, res) => res.send('Hello world!'));

// use Routes
app.use('/api/books', books);

const port = process.env.PORT || 8082;

app.listen(port, () => console.log(`Server running on port ${port}`));

Running the frontend and backend

Follow the steps below to run both the frontend and backend of our MERN stack example.

Run the server

Now, run the server (inside the project folder):

$ npm run app

If you get any error, then follow the commands below (inside the project folder):

$ npm install
$ npm run app

Run the client

From the front-end project directory, run the command below:

$ npm start

If you get an error, again, follow the same commands below:

$ npm install
$ npm start

Testing our MERN stack app in the browser

Let’s check everything in the browser. Open http://localhost:3000 in your browser. Now you can add a book, delete a book, show the list of books, and edit books. The following routes should perform accordingly:

Add a new book: http://localhost:3000/create-book

The Add Book Page

Show the list of books: http://localhost:3000/

The Books List Page

Show any book’s info: http://localhost:3000/show-book/:id

Show Book Details Page

Update a book’s info: http://localhost:3000/edit-book/:id

The Edit Book Page

Congratulations! You have successfully completed this MERN stack tutorial.

You can visit my GitHub to see both the server-side and client-side portions of this MERN stack tutorial. You can also find the complete repo for our MERN stack example app on GitHub.

#mern #mongodb #react #expressjs #node #redux #mongoose #jwt #passportjs 

MERN Stack Tutorial for Beginners with Full Project
Thomas  Granger

Thomas Granger

1663294502

Learn the MERN Stack: Build a Simple CRUD App from Scratch

Learn the MERN stack by building a simple CRUD application from scratch. Learn the fundamental concepts of MongoDB, Express.js, React.js, and Node.js with hands-on.

Meanwhile, we’ll cover the following MERN stack topics:

  • What is the MERN stack?
  • Server setup with Express.js and Node.js
  • Database management with MongoDB
  • Building RESTful APIs with the MERN stack
  • Building the frontend
  • Setting up Create React App
  • Initial project structure
  • Frontend tasks and features
  • Adding feature components
  • Connecting the frontend and backend
  • Running the frontend and backend
  • Testing our MERN stack app in the browser

This demo is designed to highlight the MERN setup. The objective is to develop a simple project with the best possible structure so that you can use it as a boilerplate and elevate your MERN stack projects to meet industry standards.

What is the MERN stack?

The phrase MERN stack refers to the following technologies:

  • MongoDB, a cross-platform document-oriented database program
  • Express.js, a web application framework for Node.js
  • React, a JavaScript library for building user interfaces
  • Node.js, an open-source, cross-platform JavaScript run-time environment that executes JavaScript code outside of a browser

If you’re a visual learner (and have some time on your hands), check out this comprehensive, up-to-date MERN stack video tutorial:

Server setup with Express.js and Node.js

To begin our MERN stack tutorial, we’ll show you how to set up a server with Express.js and Node.js.

npm package initialization

To create a project folder, enter the folder through the terminal, then run the following command:

$ npm init

Now it will ask you some questions about package name, version, entry point, etc. Hit enter if you want to keep the default. After that, you will get something like this:

Creating Our package.json File

Select yes and you’re ready to go. It creates a file named package.json.

Installing dependencies

Now, I would like to add some dependencies:

$ npm i express mongoose body-parser bcryptjs validation

Type or copy the command above and hit the enter button. You’ll see something like this:

Adding Project File Dependencies

  • bcryptjs is a password hashing function designed by Niels Provos and David Mazières
  • body-parser allows us to get the data throughout the request
  • express is our main framework
  • mongoose is used to connect/interact with MongoDB
  • validation (as its name implies) is used for validation

Now I want to add nodemon as a dev dependency. If you don’t want to add this, you can skip it — it’s optional.

$ npm i -D nodemon

nodemon is a utility that will monitor for any changes in your source and automatically restart your server.

At that point, your package.json should look like this:

package.json File With Dependencies

Setting the entry point

Now create a file named app.js for our entry point. You can create this from the project folder with the command below (on Mac):

$ touch app.js

Then paste the code below:

// app.js

const express = require('express');

const app = express();

app.get('/', (req, res) => res.send('Hello world!'));

const port = process.env.PORT || 8082;

app.listen(port, () => console.log(`Server running on port ${port}`));

Now, run the command

$ node app

You will see Server running on port 8082. You can also check it from the browser: open the browser and enter http://localhost:8082.

At this point, if we change anything, we need to restart the server manually. But if we set up nodemon, then we don’t have to restart it every time; nodemon will watch if there is any change and restart the server automatically.

So what you need to do for that is a little change to the scripts in our package.json file. See below:

// package.json

{
  "name": "mern_a_to_z",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "start": "node app.js",
    "app": "nodemon app.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/nurislam03/MERN_A_to_Z.git"
  },
  "author": "Nur Islam",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/nurislam03/MERN_A_to_Z/issues"
  },
  "homepage": "https://github.com/nurislam03/MERN_A_to_Z#readme",
  "dependencies": {
    "bcryptjs": "^2.4.3",
    "body-parser": "^1.19.0",
    "express": "^4.17.1",
    "mongoose": "^5.5.15",
    "validation": "0.0.1"
  },
  "devDependencies": {
    "nodemon": "^1.19.1"
  }
}

So, now you can run your project using this command:

$ npm run app

If you get any error at this point, then run the commands below:

$ npm install
$ npm run app

You will see the following changes in your terminal if everything goes right:

Running Project Successfully

Database management with MongoDB

Now it’s time to work on our MERN database setup with MongoDB. For simplicity, we will use MongoDB Atlas.

Creating an account for MongoDB Atlas

MongoDB Atlas is a fully managed cloud database developed by the same team that built MongoDB.

First, you need an account. Create one and follow the procedure. After creating an account, you will see something like this:

MongoDB Atlas Homescreen

Click on the Project 0 section (top left) and you will see a button for creating a new project. Create a project and select the project.

Now, click on the Build a Cluster button from the project you have created. It will show you all the information. At the bottom, you will see a section called Cluster Name, click on that and enter a name for the database, then hit the Create Cluster button.

After two to three minutes, if everything goes well, you will find something like this:

Creating A Cluster In MongoDB Atlas

Click on the CONNECT button and fill in the username and password form for your database.

Setting Up Our Connection

Now hit the Create MongoDB User button. You can also choose either your current IP address or a different IP address, it’s up to you.

Now, if you follow the CONNECT button or the Choose a connection method button, you will see some different methods. Select accordingly.

Connection Methods Options

In this case, select the Connect Your Application section.

Now you will get your database link, which we will use in our next step.

Connection String Output

Our database is ready — now we need to add it to our project.

Inside the project folder, create another folder named config and inside it create two files named default.json and db.js. Add the following code:

// default.json

{
  "mongoURI":
    "mongodb+srv://mern123:<password>@mernatoz-9kdpd.mongodb.net/test?retryWrites=true&w=majority"
}
 /* Replace <password> with your database password */

/* ------------------------------------------------------------------ */
// db.js

const mongoose = require('mongoose');
const config = require('config');
const db = config.get('mongoURI');

const connectDB = async () => {
  try {
    await mongoose.connect(
      db,
      {
        useNewUrlParser: true
      }
    );

    console.log('MongoDB is Connected...');
  } catch (err) {
    console.error(err.message);
    process.exit(1);
  }
};

module.exports = connectDB;

NOTE: We need a little change in our app.js file to connect to the database. Update your app.js with this:

// app.js

const express = require('express');
const connectDB = require('./config/db');

const app = express();

// Connect Database
connectDB();

app.get('/', (req, res) => res.send('Hello world!'));

const port = process.env.PORT || 8082;

app.listen(port, () => console.log(`Server running on port ${port}`));

We need another dependency package called config for the global variable to run our project. Use the following command to install it to the project:

$ npm i config

Now, you can run the project using the following command:

$ npm run app
Successfully Connected Server

Great! So far we are on the right track. Our database is successfully connected. Now time to complete the route setup, and after that, we will see how to create RESTful APIs.

Building RESTful APIs with the MERN stack

Create a folder named routes. In it, create another folder named api, which will hold all our APIs.

Inside the api folder, create a file named books.js. We will create some APIs here to show how it works in a moment.

Now update your books.js with the following code:

// routes/api/books.js

const express = require('express');
const router = express.Router();

// Load Book model
const Book = require('../../models/Book');

// @route GET api/books/test
// @description tests books route
// @access Public
router.get('/test', (req, res) => res.send('book route testing!'));

// @route GET api/books
// @description Get all books
// @access Public
router.get('/', (req, res) => {
  Book.find()
    .then(books => res.json(books))
    .catch(err => res.status(404).json({ nobooksfound: 'No Books found' }));
});

// @route GET api/books/:id
// @description Get single book by id
// @access Public
router.get('/:id', (req, res) => {
  Book.findById(req.params.id)
    .then(book => res.json(book))
    .catch(err => res.status(404).json({ nobookfound: 'No Book found' }));
});

// @route GET api/books
// @description add/save book
// @access Public
router.post('/', (req, res) => {
  Book.create(req.body)
    .then(book => res.json({ msg: 'Book added successfully' }))
    .catch(err => res.status(400).json({ error: 'Unable to add this book' }));
});

// @route GET api/books/:id
// @description Update book
// @access Public
router.put('/:id', (req, res) => {
  Book.findByIdAndUpdate(req.params.id, req.body)
    .then(book => res.json({ msg: 'Updated successfully' }))
    .catch(err =>
      res.status(400).json({ error: 'Unable to update the Database' })
    );
});

// @route GET api/books/:id
// @description Delete book by id
// @access Public
router.delete('/:id', (req, res) => {
  Book.findByIdAndRemove(req.params.id, req.body)
    .then(book => res.json({ mgs: 'Book entry deleted successfully' }))
    .catch(err => res.status(404).json({ error: 'No such a book' }));
});

module.exports = router;

Database model

In order to interact with our database, we need to create a model for each of our resources. So, create a folder called models in the root, and inside the models folder, create a file called Book.js and update it with this:

// models/Book.js

const mongoose = require('mongoose');

const BookSchema = new mongoose.Schema({
  title: {
    type: String,
    required: true
  },
  isbn: {
    type: String,
    required: true
  },
  author: {
    type: String,
    required: true
  },
  description: {
    type: String
  },
  published_date: {
    type: Date
  },
  publisher: {
    type: String
  },
  updated_date: {
    type: Date,
    default: Date.now
  }
});

module.exports = Book = mongoose.model('book', BookSchema);

Run the project to see if everything is fine at this point, and you can test all the APIs through Postman (note that before testing APIs using Postman, you need to run the project first). You can download Postman here.


Building the frontend

So far, so good! Now that we’ve set up our backend, it’s time to transition to the frontend part of this MERN stack tutorial.

In this section, we’ll use React to build our user interfaces. React is a JavaScript library for building user interfaces. It is maintained by Facebook and a community of individual developers and other companies.

We’ll use Create React App to generate our initial file setup. CRA is a comfortable environment for learning React and is the best way to start building applications in React. It offers a modern build setup with no configuration.

We’ll also use webpack and Babel to bundle our modules and compile our JavaScript, respectively. If you don’t know webpack or Babel well, no problem; you don’t need to install or configure tools like webpack or Babel. They’re preconfigured and hidden so that you can focus on the code. Just create a project, and you’re good to go.

You’ll also need any version of Node.js greater than 8.10 and any version of npm greater than 5.6 installed on your local development machine.

Setting up Create React App

Set any directory using a terminal where you want to keep all the files of this project and run the following command to get the initial setup file:

 $ npx create-react-app my-app

You can replace my-app with whatever you’d like to use as your project name. For example, my project name is mern_a_to_z_client, and my command is:

 $ npx create-react-app mern_a_to_z_client

Note: The project name must be in lowercase letters.

If everything goes right, then you will see something like the following image, where you will find some instructions along with the commands.

Project Successfully Created In Create React App

Before using any built-in command, we need to go inside the project folder.

 $ cd mern_a_to_z_client

Now that we are in the project directory, we can use those available commands. If you’re using Yarn:

$ yarn start

Or, if using npm:

$ npm start

To run the app in development mode, you can use any of the above commands, and you will see the following message in your terminal.

Running App In Development Mode

Now open http://localhost:3000 to view it in the browser. This page will automatically reload if you make changes to the code.

Viewing Our App In The Browser

Initial project structure

Inside the project directory, our initial file structure should look like this:

Our Initial Project Structure

Adding Bootstrap and Font Awesome to your React app

We have got our initial setup file for the front-end part. Now we can start integrating our back end with our front end. Before that, though, I want to add Bootstrap and Font Awesome’s CDN to our project.

Open the file called index.html, which is in the public folder mern_a_to_z_client/public/index.html, and replace everything with the following code:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->

    <!-- bootstrap css cdn -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

    <!-- fontawesome cdn -->
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css" integrity="sha384-hWVjflwFxL6sNzntih27bfxkr27PmbbK/iSvJ+a4+0owXq79v+lsFkW54bOGbiDQ" crossorigin="anonymous">

    <title>MERN A to Z</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->

    <!-- bootstrap JS cdn -->
    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>

  </body>
</html>

Frontend tasks and features

We will work with five different features:

  1. Add, create or save a new book
  2. Show all the books we have stored in the database
  3. Show a single book
  4. Update a book
  5. Delete a book

Dependencies packages installation

Now, use the following command to add some necessary dependencies:

$ npm install --save react-router-dom
$ npm install --save axios

Why Axios?

Axios is a lightweight HTTP client based similar to a Fetch API. Axios is a promise-based async/await library for readable asynchronous code. We can easily integrate with React, and it is effortless to use in any front-end framework.

We’ll call our APIs through Axios.

Package.json file

At this point, our package.json file should be similar to this; versions can be similar or different:

// MERN_A_to_Z_Client - package.json

{
  "name": "mern_a_to_z_client",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "axios": "^0.19.0",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-router-dom": "^5.0.1",
    "react-scripts": "3.0.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

Creating the component file

Inside the src folder (mern_a_to_z_client/src/), create another folder called components, and inside it, create five different files:

  1. CreateBook.js
  2. ShowBookList.js
  3. BookCard.js
  4. ShowBookDetails.js
  5. UpdateBookInfo.js

We will work with these five files a bit later.

Setup route

Open the folder called App.js inside the src folder (mern_a_to_z_client/src/App.js), and replace it with the following code:

import React, { Component } from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import './App.css';

import CreateBook from './components/CreateBook';
import ShowBookList from './components/ShowBookList';
import ShowBookDetails from './components/ShowBookDetails';
import UpdateBookInfo from './components/UpdateBookInfo';

class App extends Component {
  render() {
    return (
      <Router>
        <div>
          <Route exact path='/' component={ShowBookList} />
          <Route path='/create-book' component={CreateBook} />
          <Route path='/edit-book/:id' component={UpdateBookInfo} />
          <Route path='/show-book/:id' component={ShowBookDetails} />
        </div>
      </Router>
    );
  }
}

export default App;

Here, we define all the routes. For a specific path definition, its corresponding component will be rendered. We have not implemented these files/components yet — just completed the path setup.

Updating the CSS file

Update a CSS file called App.css in the src folder with the following code:

.App {
  text-align: center;
}

.App-logo {
  animation: App-logo-spin infinite 20s linear;
  height: 40vmin;
  pointer-events: none;
}

.App-header {
  background-color: #282c34;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: calc(10px + 2vmin);
  color: white;
}

.App-link {
  color: #61dafb;
}

@keyframes App-logo-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

.CreateBook {
  background-color: #2c3e50;
  min-height: 100vh;
  color: white;
}

.ShowBookDetails {
  background-color: #2c3e50;
  min-height: 100vh;
  color: white;
}

.UpdateBookInfo {
  background-color: #2c3e50;
  min-height: 100vh;
  color: white;
}

.ShowBookList {
  background-color: #2c3e50;
  height: 100%;
  width: 100%;
  min-height: 100vh;
  min-width: 100px;
  color: white;
}


/* BookList Styles */
.list {
  display: grid;
  margin: 20px 0 50px 0;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 1fr;
  grid-gap: 2em;
}

.card-container {
  width: 250px;
  border: 1px solid rgba(0,0,.125);
  margin: 0 auto;
  border-radius: 5px;
  overflow: hidden;
}

.desc {
  height: 130px;
  padding: 10px;
}

.desc h2 {
  font-size: 1em;
  font-weight: 400;
}

.desc h3, p {
  font-weight: 300;
}

.desc h3 {
  color: #6c757d;
  font-size: 1em;
  padding: 10px 0 10px 0;
}

Adding our feature components

Now it’s time to add feature components to our MERN stack project.

Create a new book

Our CreateBook.js file is responsible for adding, creating, or saving a new book or a book’s info. So, update CreateBook.js with the following code:

import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import '../App.css';
import axios from 'axios';


class CreateBook extends Component {
  constructor() {
    super();
    this.state = {
      title: '',
      isbn:'',
      author:'',
      description:'',
      published_date:'',
      publisher:''
    };
  }

  onChange = e => {
    this.setState({ [e.target.name]: e.target.value });
  };

  onSubmit = e => {
    e.preventDefault();

    const data = {
      title: this.state.title,
      isbn: this.state.isbn,
      author: this.state.author,
      description: this.state.description,
      published_date: this.state.published_date,
      publisher: this.state.publisher
    };

    axios
      .post('http://localhost:8082/api/books', data)
      .then(res => {
        this.setState({
          title: '',
          isbn:'',
          author:'',
          description:'',
          published_date:'',
          publisher:''
        })
        this.props.history.push('/');
      })
      .catch(err => {
        console.log("Error in CreateBook!");
      })
  };

  render() {
    return (
      <div className="CreateBook">
        <div className="container">
          <div className="row">
            <div className="col-md-8 m-auto">
              <br />
              <Link to="/" className="btn btn-outline-warning float-left">
                  Show BooK List
              </Link>
            </div>
            <div className="col-md-8 m-auto">
              <h1 className="display-4 text-center">Add Book</h1>
              <p className="lead text-center">
                  Create new book
              </p>

              <form noValidate onSubmit={this.onSubmit}>
                <div className='form-group'>
                  <input
                    type='text'
                    placeholder='Title of the Book'
                    name='title'
                    className='form-control'
                    value={this.state.title}
                    onChange={this.onChange}
                  />
                </div>
                <br />

                <div className='form-group'>
                  <input
                    type='text'
                    placeholder='ISBN'
                    name='isbn'
                    className='form-control'
                    value={this.state.isbn}
                    onChange={this.onChange}
                  />
                </div>

                <div className='form-group'>
                  <input
                    type='text'
                    placeholder='Author'
                    name='author'
                    className='form-control'
                    value={this.state.author}
                    onChange={this.onChange}
                  />
                </div>

                <div className='form-group'>
                  <input
                    type='text'
                    placeholder='Describe this book'
                    name='description'
                    className='form-control'
                    value={this.state.description}
                    onChange={this.onChange}
                  />
                </div>

                <div className='form-group'>
                  <input
                    type='date'
                    placeholder='published_date'
                    name='published_date'
                    className='form-control'
                    value={this.state.published_date}
                    onChange={this.onChange}
                  />
                </div>
                <div className='form-group'>
                  <input
                    type='text'
                    placeholder='Publisher of this Book'
                    name='publisher'
                    className='form-control'
                    value={this.state.publisher}
                    onChange={this.onChange}
                  />
                </div>

                <input
                    type="submit"
                    className="btn btn-outline-warning btn-block mt-4"
                />
              </form>
          </div>
          </div>
        </div>
      </div>
    );
  }
}

export default CreateBook;

Show all books

The ShowBookList.js component will be responsible for showing all the books we already have stored in our database. Update ShowBookList.js with this code:

import React, { Component } from 'react';
import '../App.css';
import axios from 'axios';
import { Link } from 'react-router-dom';
import BookCard from './BookCard';

class ShowBookList extends Component {
  constructor(props) {
    super(props);
    this.state = {
      books: []
    };
  }

  componentDidMount() {
    axios
      .get('http://localhost:8082/api/books')
      .then(res => {
        this.setState({
          books: res.data
        })
      })
      .catch(err =>{
        console.log('Error from ShowBookList');
      })
  };


  render() {
    const books = this.state.books;
    console.log("PrintBook: " + books);
    let bookList;

    if(!books) {
      bookList = "there is no book record!";
    } else {
      bookList = books.map((book, k) =>
        <BookCard book={book} key={k} />
      );
    }

    return (
      <div className="ShowBookList">
        <div className="container">
          <div className="row">
            <div className="col-md-12">
              <br />
              <h2 className="display-4 text-center">Books List</h2>
            </div>

            <div className="col-md-11">
              <Link to="/create-book" className="btn btn-outline-warning float-right">
                + Add New Book
              </Link>
              <br />
              <br />
              <hr />
            </div>

          </div>

          <div className="list">
                {bookList}
          </div>
        </div>
      </div>
    );
  }
}

export default ShowBookList;

Creating a card for each book

Here we use a functional component called BookCard.js, which takes a book’s info from ShowBookList.js and makes a card for each book. Write the following code to update your BookCard.js file:

import React from 'react';
import { Link } from 'react-router-dom';
import '../App.css';

const BookCard = (props) => {
    const  book  = props.book;

    return(
        <div className="card-container">
            <img src="https://commapress.co.uk/books/the-book-of-cairo/cairo-provisional-v3/image%2Fspan3" alt="" />
            <div className="desc">
                <h2>
                    <Link to={`/show-book/${book._id}`}>
                        { book.title }
                    </Link>
                </h2>
                <h3>{book.author}</h3>
                <p>{book.description}</p>
            </div>
        </div>
    )
};

export default BookCard;

NOTE: Here, I used the same img src for each book, since each book’s respective image may not always be available. Change the image source, and you can also use a different image for each book.

Show a book’s info

The ShowBookDetails component has one task: it shows all the info we have about any book. We have both delete and edit buttons here to get access.

import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import '../App.css';
import axios from 'axios';

class showBookDetails extends Component {
  constructor(props) {
    super(props);
    this.state = {
      book: {}
    };
  }

  componentDidMount() {
    // console.log("Print id: " + this.props.match.params.id);
    axios
      .get('http://localhost:8082/api/books/'+this.props.match.params.id)
      .then(res => {
        // console.log("Print-showBookDetails-API-response: " + res.data);
        this.setState({
          book: res.data
        })
      })
      .catch(err => {
        console.log("Error from ShowBookDetails");
      })
  };

  onDeleteClick (id) {
    axios
      .delete('http://localhost:8082/api/books/'+id)
      .then(res => {
        this.props.history.push("/");
      })
      .catch(err => {
        console.log("Error form ShowBookDetails_deleteClick");
      })
  };


  render() {

    const book = this.state.book;
    let BookItem = <div>
      <table className="table table-hover table-dark">
        {/* <thead>
          <tr>
            <th scope="col">#</th>
            <th scope="col">First</th>
            <th scope="col">Last</th>
            <th scope="col">Handle</th>
          </tr>
        </thead> */}
        <tbody>
          <tr>
            <th scope="row">1</th>
            <td>Title</td>
            <td>{ book.title }</td>
          </tr>
          <tr>
            <th scope="row">2</th>
            <td>Author</td>
            <td>{ book.author }</td>
          </tr>
          <tr>
            <th scope="row">3</th>
            <td>ISBN</td>
            <td>{ book.isbn }</td>
          </tr>
          <tr>
            <th scope="row">4</th>
            <td>Publisher</td>
            <td>{ book.publisher }</td>
          </tr>
          <tr>
            <th scope="row">5</th>
            <td>Published Date</td>
            <td>{ book.published_date }</td>
          </tr>
          <tr>
            <th scope="row">6</th>
            <td>Description</td>
            <td>{ book.description }</td>
          </tr>
        </tbody>
      </table>
    </div>

    return (
      <div className="ShowBookDetails">
        <div className="container">
          <div className="row">
            <div className="col-md-10 m-auto">
              <br /> <br />
              <Link to="/" className="btn btn-outline-warning float-left">
                  Show Book List
              </Link>
            </div>
            <br />
            <div className="col-md-8 m-auto">
              <h1 className="display-4 text-center">Book's Record</h1>
              <p className="lead text-center">
                  View Book's Info
              </p>
              <hr /> <br />
            </div>
          </div>
          <div>
            { BookItem }
          </div>

          <div className="row">
            <div className="col-md-6">
              <button type="button" className="btn btn-outline-danger btn-lg btn-block" onClick={this.onDeleteClick.bind(this,book._id)}>Delete Book</button><br />
            </div>

            <div className="col-md-6">
              <Link to={`/edit-book/${book._id}`} className="btn btn-outline-info btn-lg btn-block">
                    Edit Book
              </Link>
              <br />
            </div>

          </div>
            {/* <br />
            <button type="button" class="btn btn-outline-info btn-lg btn-block">Edit Book</button>
            <button type="button" class="btn btn-outline-danger btn-lg btn-block">Delete Book</button> */}

        </div>
      </div>
    );
  }
}

export default showBookDetails;

Update a book’s info

UpdateBookInfo.js, as its name indicates, is responsible for updating a book’s info. An Edit Book button will trigger this component to perform. After clicking Edit Book, we will see a form with the old info, which we will be able to edit or replace.

import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import axios from 'axios';
import '../App.css';

class UpdateBookInfo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      title: '',
      isbn: '',
      author: '',
      description: '',
      published_date: '',
      publisher: ''
    };
  }

  componentDidMount() {
    // console.log("Print id: " + this.props.match.params.id);
    axios
      .get('http://localhost:8082/api/books/'+this.props.match.params.id)
      .then(res => {
        // this.setState({...this.state, book: res.data})
        this.setState({
          title: res.data.title,
          isbn: res.data.isbn,
          author: res.data.author,
          description: res.data.description,
          published_date: res.data.published_date,
          publisher: res.data.publisher
        })
      })
      .catch(err => {
        console.log("Error from UpdateBookInfo");
      })
  };

  onChange = e => {
    this.setState({ [e.target.name]: e.target.value });
  };

  onSubmit = e => {
    e.preventDefault();

    const data = {
      title: this.state.title,
      isbn: this.state.isbn,
      author: this.state.author,
      description: this.state.description,
      published_date: this.state.published_date,
      publisher: this.state.publisher
    };

    axios
      .put('http://localhost:8082/api/books/'+this.props.match.params.id, data)
      .then(res => {
        this.props.history.push('/show-book/'+this.props.match.params.id);
      })
      .catch(err => {
        console.log("Error in UpdateBookInfo!");
      })
  };


  render() {
    return (
      <div className="UpdateBookInfo">
        <div className="container">
          <div className="row">
            <div className="col-md-8 m-auto">
              <br />
              <Link to="/" className="btn btn-outline-warning float-left">
                  Show BooK List
              </Link>
            </div>
            <div className="col-md-8 m-auto">
              <h1 className="display-4 text-center">Edit Book</h1>
              <p className="lead text-center">
                  Update Book's Info
              </p>
            </div>
          </div>

          <div className="col-md-8 m-auto">
          <form noValidate onSubmit={this.onSubmit}>
            <div className='form-group'>
              <label htmlFor="title">Title</label>
              <input
                type='text'
                placeholder='Title of the Book'
                name='title'
                className='form-control'
                value={this.state.title}
                onChange={this.onChange}
              />
            </div>
            <br />

            <div className='form-group'>
            <label htmlFor="isbn">ISBN</label>
              <input
                type='text'
                placeholder='ISBN'
                name='isbn'
                className='form-control'
                value={this.state.isbn}
                onChange={this.onChange}
              />
            </div>

            <div className='form-group'>
            <label htmlFor="author">Author</label>
              <input
                type='text'
                placeholder='Author'
                name='author'
                className='form-control'
                value={this.state.author}
                onChange={this.onChange}
              />
            </div>

            <div className='form-group'>
            <label htmlFor="description">Description</label>
              <input
                type='text'
                placeholder='Describe this book'
                name='description'
                className='form-control'
                value={this.state.description}
                onChange={this.onChange}
              />
            </div>

            <div className='form-group'>
            <label htmlFor="published_date">Published Date</label>
              <input
                type='date'
                placeholder='published_date'
                name='published_date'
                className='form-control'
                value={this.state.published_date}
                onChange={this.onChange}
              />
            </div>
            <div className='form-group'>
            <label htmlFor="publisher">Publisher</label>
              <input
                type='text'
                placeholder='Publisher of this Book'
                name='publisher'
                className='form-control'
                value={this.state.publisher}
                onChange={this.onChange}
              />
            </div>

            <button type="submit" className="btn btn-outline-info btn-lg btn-block">Update Book</button>
            </form>
          </div>

        </div>
      </div>
    );
  }
}

export default UpdateBookInfo;

Connecting the frontend to the backend

We just implemented all of our components! Now we need a little change in our server-side (back-end) project.

Changes required on the backend

If we try to call our back-end API from the front-end part, it gets an error that says: “Access to XMLHttpRequest at ‘http://localhost:8082/api/books&#8217; from origin ‘http://localhost:3000&#8217; has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.”

To solve this, we need to install cors in our back-end (server-side) project. Go to the project folder (e.g., MERN_A_to_Z) and run:

$ npm install cors

Now, update app.js (the back end’s entry point) with the following code:

// app.js

const express = require('express');
const connectDB = require('./config/db');
var cors = require('cors');

// routes
const books = require('./routes/api/books');

const app = express();

// Connect Database
connectDB();

// cors
app.use(cors({ origin: true, credentials: true }));

// Init Middleware
app.use(express.json({ extended: false }));

app.get('/', (req, res) => res.send('Hello world!'));

// use Routes
app.use('/api/books', books);

const port = process.env.PORT || 8082;

app.listen(port, () => console.log(`Server running on port ${port}`));

Running the frontend and backend

Follow the steps below to run both the frontend and backend of our MERN stack example.

Run the server

Now, run the server (inside the project folder):

$ npm run app

If you get any error, then follow the commands below (inside the project folder):

$ npm install
$ npm run app

Run the client

From the front-end project directory, run the command below:

$ npm start

If you get an error, again, follow the same commands below:

$ npm install
$ npm start

Testing our MERN stack app in the browser

Let’s check everything in the browser. Open http://localhost:3000 in your browser. Now you can add a book, delete a book, show the list of books, and edit books. The following routes should perform accordingly:

Add a new book: http://localhost:3000/create-book

The Add Book Page

Show the list of books: http://localhost:3000/

The Books List Page

Show any book’s info: http://localhost:3000/show-book/:id

Show Book Details Page

Update a book’s info: http://localhost:3000/edit-book/:id

The Edit Book Page

Congratulations! You have successfully completed this MERN stack tutorial.

You can visit my GitHub to see both the server-side and client-side portions of this MERN stack tutorial. You can also find the complete repo for our MERN stack example app on GitHub.

#mern #mongodb #expressjs #node #react 

Learn the MERN Stack: Build a Simple CRUD App from Scratch
Code  JS

Code JS

1662866893

How To Reset Password In Mern Stack | Send Reset Password Link Using Nodemailer

Forgot Password and Reset Password IN MERN Stack,Send Reset Password Link Using Nodemailer

In This Video We Will See How To Reset Password In Mern Stack. We Will See How To Send Reset Password Link Using Nodemailer.

MERN stands for MongoDB, Express, React, Node, after the four key technologies that make up the stack.

  • MongoDB — document database
  • Express(.js) — Node.js web framework
  • React(.js) — a client-side JavaScript framework
  • Node(.js) — the premier JavaScript web server
     

Express and Node make up the middle (application) tier. Express.js is a server-side web framework, and Node.js is the popular and powerful JavaScript server platform. Regardless of which variant you choose, ME(RVA)N is the ideal approach to working with JavaScript and JSON, all the way through.

Frontend - React.JS
Backend - Node.JS & Express.JS
Database - MongoDB, MongoDB Atlas
Authentication :- JWT Authentication
IDE used - Visual Studio Code

Source Code :- https://github.com/harsh17112000/MERN_forgot_password 

Subscribe: https://www.youtube.com/c/HarshPathakNV/featured 

#react #nodejs #mern #expressjs

How To Reset Password In Mern Stack | Send Reset Password Link Using Nodemailer
Jacob Banks

Jacob Banks

1662537557

Build a Password Manager with React, Node, ExpressJS and MySQL

This tutorial shows how to build a password manager using ReactJS, NodeJS, Express, and MySQL. Passwords will be encrypted using CryptoJS and our UI will be done with pure CSS (despite in the beginning of the video mentioning material ui).

Build a Password Manager in React, MySQL, and NodeJS - Ep 1

Build a Password Manager in React, MySQL, and NodeJS Ep 2 - Password Encryption

Build a Password Manager in React, MySQL, and NodeJS Ep 3 - Decryption

Code: https://github.com/machadop1407/password-manager-react-mysql 
 

#security #node #react #mysql #expressjs 

Build a Password Manager with React, Node, ExpressJS and MySQL
Corey Brooks

Corey Brooks

1661736583

Build a CRUD API with TypeScript, Express, MongoDB, Zod and Jest

This tutorial shows how to build a CRUD API with TypeScript, Express, MongoDB, Zod and Jest

00:00:00 Intro
00:00:09 Hello Friends
00:01:00 Project Template
00:01:40 Clone Template
00:02:21 Template Overview
00:05:00 Typescript with Express
00:07:23 How to find Typescript libraries
00:09:21 Install zod and mongodb
00:09:43 Folder Structure
00:11:36 Create a schema with zod
00:14:39 Create todos router
00:18:59 zod default values
00:19:20 Restructure tests
00:21:31 Our first test
00:25:35 mongodb setup 
00:29:23 collection / model setup
00:31:00 findAll handler
00:33:22 test beforeAll collection drop
00:34:47 close database connection after tests
00:36:23 jest and supertest explained
00:38:01 refactor request handlers
00:39:57 Service / Controller / DAL explained / best practices
00:43:25 Express Error Handling
00:46:18 createOne handler
00:49:55 createOne tests
00:58:30 tests are nice
00:59:01 request validation middleware refactor 
01:07:40 findOne handler
01:13:09 findOne tests
01:16:00 zod ObjectId validation
01:20:43 updateOne handler setup
01:22:57 updateOne tests
01:27:23 updateOne handler implementation
01:29:02 deleteOne handler setup
01:30:28 deleteOne tests 
01:32:29 deleteOne handler implementation
01:36:13 we did it
01:36:44 findAll correction
01:37:49 Thank You!
01:38:40 catJAM


Express API Starter with Typescript

How to use this template:

npx create-express-api --typescript --directory my-api-name

Includes API Server utilities:

  • morgan
    • HTTP request logger middleware for node.js
  • helmet
    • Helmet helps you secure your Express apps by setting various HTTP headers. It's not a silver bullet, but it can help!
  • dotenv
    • Dotenv is a zero-dependency module that loads environment variables from a .env file into process.env
  • cors
    • CORS is a node.js package for providing a Connect/Express middleware that can be used to enable CORS with various options.

Development utilities:

  • typescript
    • TypeScript is a language for application-scale JavaScript.
  • ts-node
    • TypeScript execution and REPL for node.js, with source map and native ESM support.
  • nodemon
    • nodemon is a tool that helps develop node.js based applications by automatically restarting the node application when file changes in the directory are detected.
  • eslint
    • ESLint is a tool for identifying and reporting on patterns found in ECMAScript/JavaScript code.
  • typescript-eslint
    • Tooling which enables ESLint to support TypeScript.
  • jest
    • Jest is a delightful JavaScript Testing Framework with a focus on simplicity.
  • supertest
    • HTTP assertions made easy via superagent.

Setup

npm install

Lint

npm run lint

Test

npm run test

Development

npm run dev

View the code here: https://github.com/CodingGarden/intro-to-typescript/tree/examples/examples/express-api

#typescript #expressjs #mongodb #zod #jest #api #crud 

Build a CRUD API with TypeScript, Express, MongoDB, Zod and Jest
Jade Bird

Jade Bird

1661564624

Building a Full-Stack Web App with ReactJS, NodeJS, Express & MySQL

In this Full Stack Web Development Course, you'll learn how to create a full stack web app from scratch with ReactJS, NodeJS, Express & MySQL

In this tutorial, you'll learn:

  1. Introduction - Full Stack Web Development Course [1] | ReactJS, NodeJS, Express, MySQL
  2. GET and POST Requests - Full Stack Web Development Course [2] | ReactJS, NodeJS, MySQL
  3. Connecting React to NodeJS - Full Stack Web Development Course [3] | ReactJS, NodeJS, MySQL
  4. Form Validation - Full Stack Web Development Course [4] | ReactJS, NodeJS, MySQL
  5. Full Stack Web Development Course [5] | ReactJS, NodeJS, MySQL - Individual Pages Based on ID
  6. Full Stack Web Development Course [6] | ReactJS, NodeJS, MySQL - Associations in Sequelize
  7. Full Stack Web Development Course [7] | ReactJS, NodeJS, MySQL - Comment Section in React
  8. Full Stack Web Development Course [8] | ReactJS, NodeJS, MySQL - Registration and Login
  9. Full Stack Web Development Course [9] | ReactJS, NodeJS, MySQL - JWT Authentication
  10. Full Stack Web Development Course [10] | ReactJS, NodeJS, MySQL - Auth in the Frontend
  11. Full Stack Web Development Course [11] | ReactJS, NodeJS, MySQL - Logging Out
  12. Full Stack Web Development Course [12] | ReactJS, NodeJS, MySQL - Liking System
  13. Full Stack Web Development Course [13] | ReactJS, NodeJS, MySQL - Page Not Found
  14. Full Stack Web Development Course [14] | ReactJS, NodeJS, MySQL - Delete a Post
  15. Full Stack Web Development Course [15] | ReactJS, NodeJS, MySQL - Profile Page
  16. Full Stack Web Development Course [16] | ReactJS, NodeJS, MySQL - Update Password
  17. Full Stack Web Development Course [17] | ReactJS, NodeJS, MySQL - Deploying the Website

Introduction - Full Stack Web Development Course [1] | ReactJS, NodeJS, Express, MySQL

GET and POST Requests - Full Stack Web Development Course [2] | ReactJS, NodeJS, MySQL

Connecting React to NodeJS - Full Stack Web Development Course [3] | ReactJS, NodeJS, MySQL

Form Validation - Full Stack Web Development Course [4] | ReactJS, NodeJS, MySQL

Full Stack Web Development Course [5] | ReactJS, NodeJS, MySQL - Individual Pages Based on ID

Full Stack Web Development Course [6] | ReactJS, NodeJS, MySQL - Associations in Sequelize

Full Stack Web Development Course [7] | ReactJS, NodeJS, MySQL - Comment Section in React

Full Stack Web Development Course [8] | ReactJS, NodeJS, MySQL - Registration and Login

Full Stack Web Development Course [9] | ReactJS, NodeJS, MySQL - JWT Authentication

Full Stack Web Development Course [10] | ReactJS, NodeJS, MySQL - Auth in the Frontend

Full Stack Web Development Course [11] | ReactJS, NodeJS, MySQL - Logging Out

Full Stack Web Development Course [12] | ReactJS, NodeJS, MySQL - Liking System

Full Stack Web Development Course [13] | ReactJS, NodeJS, MySQL - Page Not Found

Full Stack Web Development Course [14] | ReactJS, NodeJS, MySQL - Delete a Post

Full Stack Web Development Course [15] | ReactJS, NodeJS, MySQL - Profile Page

Full Stack Web Development Course [16] | ReactJS, NodeJS, MySQL - Update Password

Full Stack Web Development Course [17] | ReactJS, NodeJS, MySQL - Deploying the Website

Code: https://github.com/machadop1407/FullStack-Course/tree/Episode1/Episode2

#react #node #mysql #expressjs #reactjs #nodejs #webdev #webdevelopment #webapp 

 

Building a Full-Stack Web App with ReactJS, NodeJS, Express & MySQL
Garry Taylor

Garry Taylor

1661415212

MERN Authentication: Authentication with JWT in MERN Stack

This MERN Authentication tutorial shows how to implement authentication (using JSON web tokens), within the MERN stack

  1. MERN Authentication Tutorial #1 - Intro & Starter Project
  2. MERN Authentication Tutorial #2 - User Routes, Controller & Model
  3. MERN Authentication Tutorial #3 - Signing Up & Hashing Passwords
  4. MERN Authentication Tutorial #4 - Email & Password Validation
  5. MERN Authentication Tutorial #5 - JSON Web Tokens (theory)
  6. MERN Authentication Tutorial #6 - Signing Tokens
  7. MERN Authentication Tutorial #7 - Logging Users In
  8. MERN Authentication Tutorial #8 - React Auth Context
  9. MERN Authentication Tutorial #9 - Login & Signup Forms
  10. MERN Authentication Tutorial #10 - Making a useSignup Hook
  11. MERN Authentication Tutorial #11 - Making a useLogout Hook
  12. MERN Authentication Tutorial #12 - Making a useLogin Hook
  13. MERN Authentication Tutorial #13 - Setting the Initial Auth Status
  14. MERN Authentication Tutorial #14 - Protecting API Routes
  15. MERN Authentication Tutorial #15 - Making Authorized Requests
  16. MERN Authentication Tutorial #16 - Protecting React Routes
  17. MERN Authentication Tutorial #17 - Assigning Workouts to Users

MERN Authentication Tutorial #1 - Intro & Starter Project

MERN Authentication Tutorial #2 - User Routes, Controller & Model

In this MERN auth tutorial, you'll make a user controller & model, and set up some routes for authentication.

 

MERN Authentication Tutorial #3 - Signing Up & Hashing Passwords

MERN Authentication Tutorial #4 - Email & Password Validation

MERN Authentication Tutorial #5 - JSON Web Tokens (theory)

In this MERN Authentication tutorial you'll learn about JSON Web Tokens (JWT's) and how they work under the hood when it comes to authentication.

MERN Authentication Tutorial #6 - Signing Tokens

In this MERN Authentication tutorial, we'll see how to sign tokens and send them back to the client.

MERN Authentication Tutorial #7 - Logging Users In

MERN Authentication Tutorial #8 - React Auth Context

MERN Authentication Tutorial #9 - Login & Signup Forms

In this MERN auth tutorial, we'll flesh out the login and signup forms.

MERN Authentication Tutorial #10 - Making a useSignup Hook

In this MERN auth lesson, you'll create a custom, reusable React hook that we can use to sign new users up to the application.

MERN Authentication Tutorial #11 - Making a useLogout Hook

In this MERN auth tutorial, we'll make a custom useLogout hook to log users out of the application.

MERN Authentication Tutorial #12 - Making a useLogin Hook

In this MERN auth tutorial, we'll make a custom useLogin hook to log users in to the application.

MERN Authentication Tutorial #13 - Setting the Initial Auth Status

MERN Authentication Tutorial #14 - Protecting API Routes

Hey gang, in this MERN auth tutorial, you'll learn how to protect certain API routes from unauthenticated users.

MERN Authentication Tutorial #15 - Making Authorized Requests

MERN Authentication Tutorial #16 - Protecting React Routes

In this MERN auth tutorial we'll protect some of the React routes from users that are not authenticated.

MERN Authentication Tutorial #17 - Assigning Workouts to Users

 

🐱‍💻 Access the course files on GitHub:
https://github.com/iamshaunjp/MERN-Auth-Tutorial

#mern #mongodb #expressjs #node #react #security 

 

MERN Authentication: Authentication with JWT in MERN Stack
Callum  Owen

Callum Owen

1660036440

The Complete Guide to Node Express in 2022

The express framework is built to act as a minimal and flexible Node.js web application framework, providing a robust set of features for building single and multipage, and hybrid web applications. First, we will install the Express Framework, and then we will start building a simple web application with the backend database of MongoDB.

Express.js is a Node js web application server framework specifically designed for building single-page, multipage, and hybrid web applications.

In this tutorial, We''ll The Complete Guide to Node Express in 2022

See more at: https://appdividend.com/2022/03/05/node-js-express-tutorial-for-beginners/

#node #nodejs #express #expressjs

The Complete Guide to Node Express in 2022
Reid  Rohan

Reid Rohan

1659467760

Url_shortener_v2: URL Shortener Written in JS and Deployed on Deta

URL shortener v2

Description

This is a simple, basic, serverless URL shortener's backend, made with Deta
You can use it on https://urly.deta.dev/ 

API usage

[POST] /create

Payload sending:

{
  "uri": string
}

Receiving:

{
    "key": string, # The key of the URL
    "uri": string # The original, percent encoded URL
}

[GET] /:key

Redirect to the saved url.

Deploy

To deploy, you will need an account and project on Deta
Then, you can deploy the project to your own project by pressing this button: 
Deploy 
After filling the required options, you will be redirected to the Deta dashboard and you can get your own URL there. 
 

Others

Frontend is planned to be made soon:tm:. 
 

In case you are curious, v1 is archived and written with Python and Deta. It's not finished and currently deprecated. Repo

Original article source at: https://github.com/pickaxe828/url_shortener_v2 

#javascript #node #expressjs 

Url_shortener_v2: URL Shortener Written in JS and Deployed on Deta

URL MiniFy: A URL Shortener build with Nextjs, ExpressJS, MongoDB

📜 Overview

What is URL MiniFy?

A URL shortener build with Nextjs, ExpressJS, MongoDB

🎯 Getting Started

How to Contribute?

Contributions, issues and feature requests are welcome! Feel free to check the issues page.

✒️ Summary

  1. Fork this repository on GitHub.
  2. Clone the forked repository in your computer.

$ git clone https://github.com/<your-username>/url-minify.git

$ cd url-minify

3.   Add a remote (upstream) to the original project repository

$ git remote add upstream https://github.com/BRAVO68WEB/url-minify.git

$ git fetch upstream

$ git checkout main

$ git merge upstream/main

This makes your fork up to date with the original project repo.

Installation and Usage

4.   Go the folder where you want to contribute:

$ cd frontend OR cd backend

5.   Install the dependencies and run the development server using:

$ npm install

$ npm run start

We can start making changes now! 🥳

6.   Make your changes. Then, stage and commit them using:

$ git add .

$ git commit -m "This is a commit message"

7.   Push your changes to your forked repository.

$ git push -u origin main

8.   Go to your forked repository on GitHub. Create a pull request from the 🔃Contribute button provided there.

Contribute -> Open pull request -> Create pull request. (Make sure you write a detailed description of the changes you have made.)

And you're done! 🤩🤩

Download details:
Author: BRAVO68WEB
Source code: https://github.com/BRAVO68WEB/url-minify
License: MIT license

#next #nextjs #react #javascript #mongodb #node #expressjs 

URL MiniFy: A URL Shortener build with Nextjs, ExpressJS, MongoDB
Coding  Life

Coding Life

1658716713

How to Upload Files on an AWS S3 Bucket Using an Express Server

This video explains how to upload files to amazon s3 bucket via the SDK version 2 and 3. We are going to be creating  express api enpoints where we are going to list and upload to aws s3 buckets.

Requirements:
Basic Javascript knowlege
An AWS account with proper permissions on S3
Postman
Node js.

Subscribe: https://www.youtube.com/c/CloudGurus/featured 

#awss3  #expressjs  #javascript 

How to Upload Files on an AWS S3 Bucket Using an Express Server
许 志强

许 志强

1658298060

如何在 Node JS 中使用 Express JS 創建 GraphQL API 服務器

在本教程中,您將在Node.js中創建一個 Express API 服務器,用於提供 GraphQL 端點。您還將基於 GraphQL 類型系統構建 GraphQL 模式,包括查詢和突變等操作,以及為任何請求生成響應的解析器函數。

設置 Express HTTP 服務器

第一步是設置 Express 服務器,您可以在編寫任何 GraphQL 代碼之前執行此操作。

在一個新項目中,您將安裝expresscors使用以下npm install命令:

npm install express cors

Express將成為您服務器的框架。它是用於構建 API 的 Node.js 的 Web 應用程序框架。CORS包是源資源共享中間件,可以讓您輕鬆地從瀏覽器訪問此服務器

您還可以將 Nodemon 安裝為開發依賴項:

npm install -D nodemon

Nodemon是一個工具,它通過在檢測到目錄中的文件更改時自動重新啟動應用程序來幫助開發基於節點的應用程序。

安裝這些包將創建node_modulespackage.json列出兩個依賴項和一個開發依賴項。

使用nano或您最喜歡的文本編輯器,打開package.json進行編輯,如下所示:

{
  "dependencies": {
    "cors": "^2.8.5",
    "express": "^4.17.3"
  },
  "devDependencies": {
    "nodemon": "^2.0.15"
  }
}

此時您將添加更多字段。要package.json,進行以下突出顯示的更改:

{
  "main": "server.js",
  "scripts": {
    "dev": "nodemon server.js"
  },
  "dependencies": {
    "cors": "^2.8.5",
    "express": "^4.17.3"
  },
  "devDependencies": {
    "nodemon": "^2.0.15"
  },
  "type": "module"
}

您將在 為服務器創建一個文件server.js,因此您將main指向server.js. 這將確保npm start啟動服務器。

為了更容易在服務器上進行開發,您還創建了一個名為"dev"將運行的腳本nodemon server.js

最後,添加typeofmodule以確保您可以import在整個代碼中使用語句,而不是使用默認的 CommonJS require

完成後保存並關閉文件。

接下來,創建一個名為server.js. 在其中,您將創建一個簡單的 Express 服務器,監聽端口4000,並發送一個請求Hello, GraphQL!。要進行設置,請將以下行添加到新文件中:

import express from 'express'
import cors from 'cors'

const app = express()
const port = 4000

app.use(cors())
app.use(express.json())
app.use(express.urlencoded({ extended: true }))

app.get('/', (request, response) => {
  response.send('Hello, GraphQL!')
})

app.listen(port, () => {
  console.log(`Running a server at http://localhost:${port}`)
})

此代碼塊使用 Express 創建一個基本的 HTTP 服務器。通過調用該express函數,您可以創建一個 Express 應用程序。GET在為CORS和 JSON 設置了一些基本設置後,您將/使用app.get('/'). 最後,用於app.listen()定義 API 服務器應該監聽的端口。

完成後保存並關閉文件。

現在您可以運行命令來啟動 Node 服務器:

npm run dev

如果您http://localhost:4000在瀏覽器中訪問或運行curl http://localhost:4000命令,您會看到它返回Hello, GraphQL!,表示 Express 服務器正在運行。此時,您可以開始添加代碼以提供 GraphQL 端點。

設置 GraphQL HTTP 服務器中間件

在本節中,您將開始將 GraphQL 模式集成到基本的 Express 服務器中。您將通過定義模式、解析器並連接到數據存儲來完成此操作。

要開始將 GraphQL 集成到 Express 服務器中,您將安裝三個包:graphqlexpress-graphql@graphql-tools/schema. 運行以下命令:

npm install graphql@14 express-graphql @graphql-tools/schema
  • graphql:GraphQL 的 JavaScript 參考實現。
  • express-graphql: GraphQL 的 HTTP 服務器中間件。
  • @graphql-tools/schema:一組用於加快 GraphQL 開發的實用程序。

server.js您可以通過添加突出顯示的行將這些包導入文件中:

import express from 'express'
import cors from 'cors'
import { graphqlHTTP } from 'express-graphql'
import { makeExecutableSchema } from '@graphql-tools/schema'

...

下一步是創建一個可執行的 GraphQL 模式。

為避免設置數據庫的開銷,您可以為 GraphQL 服務器將查詢的數據使用內存存儲。data您可以使用數據庫將具有的值創建一個對象。將突出顯示的行添加到您的文件中:

import express from 'express'
import cors from 'cors'
import { graphqlHTTP } from 'express-graphql'
import { makeExecutableSchema } from '@graphql-tools/schema'

const data = {
  warriors: [
    { id: '001', name: 'Jaime' },
    { id: '002', name: 'Jorah' },
  ],
}

...

這裡的數據結構表示一個名為的數據庫表warriors,它有兩行,由JaimeJorah條目表示。

安裝好包並準備好一些數據後,您現在將創建一個模式,該模式通過描述可查詢的數據來定義 API。

GraphQL 架構

現在您已經有了一些基本數據,您可以開始為 API 製作基本架構,以獲得開始使用 GraphQL 端點所需的最少代碼量。此模式旨在復制可能用於幻想 RPG 遊戲的某些東西,其中有角色扮演戰士、巫師和治療師等角色。此示例是開放式的,因此您可以根據需要添加任意數量的內容,例如法術和武器。

GraphQL 模式依賴於類型系統。有一些內置類型,您也可以創建自己的類型。對於此示例,您將創建一個新的type名為Warrior,並為其提供兩個字段:idname

type Warrior {
  id: ID!
  name: String!
}

id有一個ID類型,而有name一個String類型。這些都是內置的標量或原始類型。感嘆號 ( !) 表示該字段不可為空,並且該類型的任何實例都需要一個值。

您需要開始的唯一附加信息是基本Query類型,它是 GraphQL 查詢的入口點。我們將定義warriors為一個Warrior類型數組。

type Query {
  warriors: [Warrior]
}

有了這兩種類型,您就有了一個可以在 GraphQL HTTP 中間件中使用的有效模式。最終,您在此處定義的模式將被傳遞到 as 提供的函數makeExecutableSchema中。傳遞給函數對象的兩個屬性如下:graphql-toolstypeDefsmakeExecutableSchema

  • typeDefs:一個 GraphQL 模式語言字符串。
  • resolvers:被調用以執行字段並產生值的函數。

server.js中,導入依賴項後,創建一個typeDefs變量並將 GraphQL 模式分配為字符串,如下所示:

...

const data = {
  warriors: [
    { id: '001', name: 'Jaime' },
    { id: '002', name: 'Jorah' },
  ],
}

const typeDefs = `
type Warrior {
  id: ID!
  name: String!
}

type Query {
  warriors: [Warrior]
}
`

...

現在您已經定義了數據集和模式,分別為datatypeDefs。接下來,您將創建解析器,以便 API 知道如何處理傳入請求。

GraphQL 解析器函數

解析器是為 GraphQL 服務器生成響應的函數集合。每個解析器函數都有四個參數:

  • obj: 父對象,這裡不需要使用,因為它已經是根對象,也就是頂級對象。
  • args:提供給該字段的任何 GraphQL 參數。
  • context: 所有解析器之間共享的狀態,通常是數據庫連接。
  • info:附加信息

在這種情況下,您將為根Query類型創建一個解析器並為warriors.

要開始使用此示例服務器,請通過將突出顯示的行添加到以下內容來傳遞本節前面的內存數據存儲server.js

...

const typeDefs = `
type Warrior {
  id: ID!
  name: String!
}

type Query {
  warriors: [Warrior]
}
`

const resolvers = {
  Query: {
    warriors: (obj, args, context, info) => context.warriors,
  },
}

...

GraphQL 服務器的入口點將通過Query解析器上的根類型。您現在已經添加了一個名為 的解析器函數,warriors它將warriorscontext. context是包含您的數據庫入口點的位置,對於此特定實現,它將是data包含您的內存數據存儲的變量。

每個單獨的解析器函數都有四個參數:objargscontextinfo。現在對我們的模式最有用和最相關的參數是context,它是解析器共享的對象。它通常用作 GraphQL 服務器和數據庫之間的連接。

最後,設置typeDefsresolvers所有設置後,您就有足夠的信息來創建可執行模式。將突出顯示的行添加到您的文件中:

...

const resolvers = {
  Query: {
    warriors: (obj, args, context, info) => context.warriors,
  },
}

const executableSchema = makeExecutableSchema({
  typeDefs,
  resolvers,
})

...

makeExecutableSchema函數創建一個完整的模式,您可以將傳遞到 GraphQL 端點。

現在,通過添加突出顯示的行,將當前返回的默認根端點替換為Hello, GraphQL!以下端點:/graphql

...

const executableSchema = makeExecutableSchema({
  typeDefs,
  resolvers,
})

app.use(
  '/graphql',
  graphqlHTTP({
    schema: executableSchema,
    context: data,
    graphiql: true,
  })
)

...

約定是 GraphQL 服務器將使用/graphql端點。使用graphqlHTTP中間件需要傳入模式和上下文,在這種情況下,它就是您的模擬數據存儲。

您現在擁有開始為端點提供服務所需的一切。您的server.js代碼應如下所示:

import express from 'express'
import cors from 'cors'
import { graphqlHTTP } from 'express-graphql'
import { makeExecutableSchema } from '@graphql-tools/schema'

const app = express()
const port = 4000

// In-memory data store
const data = {
  warriors: [
    { id: '001', name: 'Jaime' },
    { id: '002', name: 'Jorah' },
  ],
}

// Schema
const typeDefs = `
type Warrior {
  id: ID!
  name: String!
}

type Query {
  warriors: [Warrior]
}
`

// Resolver for warriors
const resolvers = {
  Query: {
    warriors: (obj, args, context) => context.warriors,
  },
}

const executableSchema = makeExecutableSchema({
  typeDefs,
  resolvers,
})

app.use(cors())
app.use(express.json())
app.use(express.urlencoded({ extended: true }))

// Entrypoint
app.use(
  '/graphql',
  graphqlHTTP({
    schema: executableSchema,
    context: data,
    graphiql: true,
  })
)

app.listen(port, () => {
  console.log(`Running a server at http://localhost:${port}`)
})

完成後保存並關閉文件。

現在您應該能夠使用GraphiQL IDEhttp://localhost:4000/graphql探索您的架構。

根據您在本節中創建的模式和解析器,您的 GraphQL API 現在已完成。在下一部分中,您將使用 GraphiQL IDE 來幫助您調試和理解您的架構。

使用 GraphiQL IDE

由於您將graphiql選項應用於trueGraphQL 中間件,因此您可以訪問 GraphiQL 集成開發環境 (IDE)。如果您在瀏覽器窗口中訪問了 GraphQL 端點,您會發現自己處於 GraphiQL 中。

GraphiQL 是一個用於編寫、驗證和測試 GraphQL 查詢的瀏覽器內工具。現在您可以測試您的 GraphQL 服務器以確保它返回正確的數據。

查詢warriors,請求idname屬性。在您的瀏覽器中,將以下行添加到 GraphiQL 的左側窗格中:

{
  warriors {
    id
    name
  }
}

按左上角的Play 箭頭提交查詢,您應該會在右側看到 JSON 格式的返回值:

Output{
  "data": {
    "warriors": [
      { "id": "001", "name": "Jaime" },
      { "id": "002", "name": "Jorah" }
    ]
  }
}

如果您刪除查詢中的某個字段,您將看到返回值相應更改。例如,如果您只想檢索name字段,則可以這樣編寫查詢:

{
  warriors {
    name
  }
}

現在您的響應將如下所示:

Output{
  "data": {
    "warriors": [{ "name": "Jaime" }, { "name": "Jorah" }]
  }
}

僅查詢您需要的字段的能力是 GraphQL 的強大方面之一,也是使其成為客戶端驅動語言的原因。

回到 GraphiQL,如果您一直點擊右側的Docs,它將展開一個標有Documentation Explorer的側邊欄。在該側邊欄中,您可以單擊文檔以更詳細地查看您的架構。

現在您的 API 已經完成,您已經探索瞭如何從 GraphiQL 使用它。下一步是從客戶端向您的 GraphQL API 發出實際請求。

從客戶端查詢 GraphQL API

就像REST API一樣,客戶端可以通過網絡發出 HTTP 請求來與 GraphQL API 進行通信。由於您可以使用內置的瀏覽器 API,例如fetch發出網絡請求,因此您也可以使用fetchGraphQL 查詢。

index.html對於一個非常基本的示例,在帶有<pre>標籤的文件中創建一個 HTML 骨架:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />

    <title>GraphQL Client</title>
  </head>

  <pre><!-- data will be displayed here --></pre>

  <body>
    <script>
      // Add query here
    </script>
  </body>
</html>

script標籤中,創建一個向 GraphQL API 發送POST請求的異步函數:

...
<body>
    <script>
      async function queryGraphQLServer() {
        const response = await fetch('http://localhost:4000/graphql', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            query: '{ warriors { name } }',
          }),
        })
        const data = await response.json()
				
        // Append data to the pre tag
        const pre = document.querySelector('pre')
        pre.textContent = JSON.stringify(data, null, 2) // Pretty-print the JSON
      }

      queryGraphQLServer()
    </script>
  </body>
...

Content-Type頭必須設置為application/json,並且查詢必須作為字符串在正文中傳遞。該腳本將調用該函數來發出請求,並在pre標籤中設置響應。

這是完整的index.html代碼。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />

    <title>GraphQL</title>
  </head>

  <pre></pre>

  <body>
    <script>
      async function queryGraphQLServer() {
        const response = await fetch('http://localhost:4000/graphql', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            query: '{ warriors { name } }',
          }),
        })
        const data = await response.json()

        const pre = document.querySelector('pre')
        pre.textContent = JSON.stringify(data, null, 2) // Pretty-print the JSON
      }

      queryGraphQLServer()
    </script>
  </body>
</html>

完成後保存並關閉文件。

現在,當您index.html在瀏覽器中查看文件時,您將看到對http://localhost:4000/graphql端點的傳出網絡請求,該請求將返回200帶有數據的 a。您可以通過打開開發人員工具並導航到網絡選項卡來查看此網絡請求。

如果您的請求通過並且您收到了200來自 GraphQL API 的數據的響應,那麼恭喜!你製作了你的第一個 GraphQL API 服務器。

結論

在本教程中,您使用 Node.js 中的 Express 框架創建了一個 GraphQL API 服務器。GraphQL 服務器由一個/graphql端點組成,可以處理傳入請求以查詢數據存儲。您的 API 有一個包含基本Query類型、自定義Warrior類型和解析器的架構,用於獲取這些類型的正確數據。

希望本文有助於揭開 GraphQL 的神秘面紗,並為使用 GraphQL 完成的工作開闢新的思路和可能性。存在許多工具可以幫助處理使用 GraphQL 的更複雜方面,例如身份驗證、安全性和緩存,但是學習如何以最簡單的方式設置 API 服務器應該有助於您了解 GraphQL 的基本要素。

原文來源https://www.digitalocean.com

#graphql #api #express #expressjs #node #nodejs 

如何在 Node JS 中使用 Express JS 創建 GraphQL API 服務器