<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ MERN Stack - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ MERN Stack - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 24 May 2026 22:24:47 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/mern/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Telehealth App Using Stream Video and Chat SDK in React ]]>
                </title>
                <description>
                    <![CDATA[ Remember when the COVID-19 pandemic moved everything online – doctor’s visits included – and staying home became the safest option?  That moment kicked off a massive shift in how healthcare gets delivered.  Telehealth became more than a workaround. I... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-telehealth-app-using-stream-video-and-chat-sdk-in-react/</link>
                <guid isPermaLink="false">687ad06430967c1524c4663e</guid>
                
                    <category>
                        <![CDATA[ streams SDK ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Streams ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tailwind CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MERN Stack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Mern Stack Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Okoro Emmanuel Nzube ]]>
                </dc:creator>
                <pubDate>Fri, 18 Jul 2025 22:53:24 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1752879185676/ab0574c8-d16b-41d0-8883-7df0f4bd0eb5.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Remember when the COVID-19 pandemic moved everything online – doctor’s visits included – and staying home became the safest option? </p>
<p>That moment kicked off a massive shift in how healthcare gets delivered. </p>
<p><a target="_blank" href="https://getstream.io/chat/solutions/healthcare/">Telehealth</a> became more than a workaround. It’s now a core part of modern care. As demand grows, developers are stepping up to build secure, real-time platforms that connect patients and providers from anywhere.</p>
<p>In this article, you’ll learn how to build a telehealth application with Stream’s <a target="_blank" href="https://getstream.io/video/sdk/react/">React Video</a> and <a target="_blank" href="https://getstream.io/chat/sdk/react/">Chat</a> SDKs. You’ll set up authentication, create video calls, enable messaging, and design a functional user interface that mimics real-world telehealth workflows.</p>
<p>Let’s dive in.</p>
<h2 id="heading-outline">Outline</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-introduction">Introduction</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-app-structure">App Structure</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-project-setup">Project Setup</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-backend-setup">Backend Setup</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-frontend-setup">Frontend Setup</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-stream-chat-and-video-integration">Stream Chat and Video Integration</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-chat-and-video-function-\(frontend\)">Chat and Video Function (Frontend)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-project-demo">Project Demo</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-Conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before you start this tutorial, make sure you have:</p>
<ul>
<li><p>A basic understanding of React.</p>
</li>
<li><p>Node.js and npm/yarn installed on your computer</p>
</li>
<li><p>A <a target="_blank" href="https://getstream.io/try-for-free/">free account with Stream</a></p>
</li>
<li><p>Familiarity with Stream SDKs</p>
</li>
<li><p>A basic understanding of Tailwind CSS for styling</p>
</li>
<li><p>Experience with VS Code and Postman (for testing APIs)</p>
</li>
</ul>
<h2 id="heading-app-structure"><strong>App Structure</strong></h2>
<p>Before diving into the code, it’s helpful to understand how the app is structured.</p>
<pre><code class="lang-markdown"><span class="hljs-section"># App Flow Structure</span>

<span class="hljs-bullet">-</span> Landing Page  
<span class="hljs-bullet">  -</span> Navigation  
<span class="hljs-bullet">    -</span> Home  
<span class="hljs-bullet">    -</span> About  
<span class="hljs-bullet">    -</span> Sign Up  
<span class="hljs-bullet">      -</span> Verify Account  
<span class="hljs-bullet">        -</span> Log In  
<span class="hljs-bullet">          -</span> Dashboard  
<span class="hljs-bullet">            -</span> Stream Chat  
<span class="hljs-bullet">            -</span> Stream Video  
<span class="hljs-bullet">            -</span> Log Out
</code></pre>
<h2 id="heading-project-setup"><strong>Project Setup</strong></h2>
<p>Before getting started, create two folders: “Frontend” to handle the client-side code and “Backend” for the server-side logic. This separation allows you to manage both parts of your application efficiently.</p>
<h3 id="heading-set-up-react-for-the-frontend"><strong>Set Up React for the Frontend</strong></h3>
<p>Once the folders are created, you can set up the React application in the Frontend folder.</p>
<p>First, navigate to the Frontend directory using the command <code>cd Frontend</code>.</p>
<p>Now you can initialize your React project. You’ll use Vite, a fast build tool for React applications.</p>
<p>To create your React project, run the following command:</p>
<p><code>npm create vite@latest [project name] -- --template react</code></p>
<p> Next, navigate to your new project folder, using the command:</p>
<p><code>cd [project name]</code></p>
<p>Once there, install the required dependencies by running:</p>
<p><code>npm install</code></p>
<p>This command installs both the <code>node_modules</code> folder (which contains all your project's packages) and the <code>package-lock.json</code> file (which records exact versions of installed packages).</p>
<p>Next, you’ll need to install Tailwind CSS for styling. Follow the <a target="_blank" href="https://v3.tailwindcss.com/docs/guides/vite">Tailwind Docs</a> for step-by-step instructions.</p>
<p>Then, it’s time to set up the website. Using React, you’ll create the home sign-in/log-in pages. Both will be nested together using <code>React-router-dom</code>.</p>
<p>Here’s what the <a target="_blank" href="https://github.com/Derekvibe/Telehealth_Frontend/tree/main/src/pages/Home">home page</a> looks like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752705718347/ed5dd289-2998-41f7-8d10-352aa35fe614.gif" alt="Telehealth Home Page" class="image--center mx-auto" width="1415" height="666" loading="lazy"></p>
<p>Now, the user has a place to land whenever they visit the website.</p>
<p>Let’s set up the backend.</p>
<h2 id="heading-backend-setup"><strong>Backend Setup</strong></h2>
<h3 id="heading-installing-required-packages"><strong>Installing Required Packages</strong></h3>
<p>Before setting up your project's backend, it's important to define what your project needs to offer. This will help you install all the necessary packages in one go.</p>
<p>Start by moving into the backend folder using the command: <code>cd Backend</code></p>
<p>Inside the Backend directory, initialize your Node.js project using <code>npm install</code></p>
<p>This will create a <code>package.json</code> file, which stores metadata and dependencies for your project.</p>
<p>Next, install all the dependencies needed to build your backend. Run the following command:</p>
<p><code>npm i bcryptjs cookie-parser cors dotenv express jsonwebtoken mongoose nodemailer validator nodemon</code></p>
<p>Here’s a brief overview of what each package does:</p>
<ul>
<li><p>bcryptjs: Encrypts user passwords for secure storage.</p>
</li>
<li><p>Cookie-parser: Handles cookies in your application.</p>
</li>
<li><p>CORS: Middleware that enables cross-origin requests – essential for frontend-backend communication.</p>
</li>
<li><p>dotenv: Loads environment variables from a <code>.env</code> file into process.env.</p>
</li>
<li><p>Express: The core framework for building your server and API routes.</p>
</li>
<li><p>jsonwebtoken: Generates and verifies JWT tokens for authentication.</p>
</li>
<li><p>Mongoose: Connects your app to a MongoDB database.</p>
</li>
<li><p>nodemailer: Handles sending emails from your application.</p>
</li>
<li><p>Validator: Validates user inputs like email, strings, and so on.</p>
</li>
<li><p>nodemon: Automatically restarts your server when changes are made to files.</p>
</li>
</ul>
<p>Once your packages are installed, create two key files in the backend directory: <code>App.js</code>, which contains your app logic, middleware, and route handlers, and <code>server.js</code>, responsible for initializing and configuring your server.</p>
<p>Next, you have to update your <code>package.json</code> start script. Head to the <code>package.json</code> file in your backend directory and replace the default script:</p>
<pre><code class="lang-json"><span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"echo\”Error: no test specified\" &amp;&amp; exit 1”
  }</span>
</code></pre>
<p>with this:</p>
<pre><code class="lang-json"><span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"nodemon server.js"</span>
  }
</code></pre>
<p>This setup allows you to run your server using <code>nodemon</code>, automatically reloading it when changes are made. This helps boost productivity during development.</p>
<p>To check if your backend setup is correct, open the <code>server.js</code> file and add a test log: <code>console.log (“Any of your Log Message”)</code>. Then, head to your terminal in the backend directory, and run npm start. You should see the log message in the terminal, confirming that your backend is running.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752703046663/dc06ce5a-3b6c-4846-bd33-53c423a57235.png" alt="Backend Server Testing" class="image--center mx-auto" width="1120" height="986" loading="lazy"></p>
<h3 id="heading-appjs-setup"><code>App.js</code> <strong>Setup</strong></h3>
<p>In the <code>App.js</code> file, start by importing the packages you initially installed.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>);

<span class="hljs-keyword">const</span> cors = <span class="hljs-built_in">require</span>(<span class="hljs-string">"cors"</span>);

<span class="hljs-keyword">const</span> cookieParser = <span class="hljs-built_in">require</span>(<span class="hljs-string">"cookie-parser"</span>);

<span class="hljs-keyword">const</span> app = express();


app.use(

  cors({

<span class="hljs-attr">origin</span>: [

   <span class="hljs-string">"http://localhost:5173"</span>,

],

<span class="hljs-attr">credentials</span>: <span class="hljs-literal">true</span>,

  })

);

app.use(express.json({ <span class="hljs-attr">limit</span>: <span class="hljs-string">"10kb"</span> }));

app.use(cookieParser());

<span class="hljs-built_in">module</span>.exports = app;
</code></pre>
<p>Here’s what the code above does: </p>
<p>The require statements import <code>express</code>, <code>cors</code>, and <code>cookie-parser</code>, which are essential for creating your backend server, handling cross-origin requests, and parsing cookies.</p>
<p>The <code>const app = express();</code> command sets up a new instance of an Express application. </p>
<p><code>app.use(cors({ origin: ["</code><a target="_blank" href="http://localhost:5173"><code>http://localhost:5173</code></a><code>"], credentials: true }));</code> grants permission or allows requests from your frontend and enables cookie sharing between the frontend and backend of your application. This is important for authentication.</p>
<p>The <code>app.use(express.json({ limit: "10kb" }));</code> command is a middleware function that ensures the server can process incoming JSON payloads and protects against overly large requests, which could be used in DoS attacks. </p>
<p>The <code>app.use(cookieParser());</code> command makes cookies available via <code>req.cookies</code>.</p>
<p>Last, the <code>module.exports = app;</code> command allows the app to be imported in other files, especially in <code>server.js</code>, which is where the app will be started.</p>
<h3 id="heading-serverjs-setup"><code>Server.js</code> <strong>Setup</strong></h3>
<p>Once <code>App.js</code> is set up, the next step is to create and configure your server in a new file called <code>server.js</code>.</p>
<p>Before doing that, ensure you have a <strong>MongoDB database</strong> set up. If you don’t have one yet, you can <a target="_blank" href="https://youtu.be/pO6m0nmo1k0?si=Rqi_50fnsfQrM-ww">follow this video tutorial</a> to set up a MongoDB database.</p>
<p>After setting up MongoDB, you will receive a <code>username</code> and <code>password</code>. Copy the password, head to your backend directory, and create a <code>.env</code> file to store it.</p>
<p>After you have stored the password, head back to complete your database setup.</p>
<p>Next, click on the “Create Database User” button, then click on the <code>choose connection method</code> option. Since we are using Node.js for this project, choose the “Drivers” option. This gives you the database connection string (you should see it at No. 3).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752706253668/ad0cdbb4-453c-4291-ab4c-395d14ce297c.gif" alt="Database-String-Auth" class="image--center mx-auto" width="1280" height="671" loading="lazy"></p>
<p>Then head to your <code>.env</code> and paste it there, and add <code>auth</code> immediately after you have “.net/”.</p>
<p>Here’s what it looks like:</p>
<p><code>mongodb+srv://&lt;username&gt;:&lt;password&gt;@cluster0.qrrtmhs.mongodb.net/auth?retryWrites=true&amp;w=majority&amp;appName=Cluster0</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752767020758/aee4f54c-e562-4916-a8f5-b97590a671d1.png" alt="Backend config.env file" class="image--center mx-auto" width="1632" height="426" loading="lazy"></p>
<p>Lastly, whitelist your IP address. This ensures your backend can connect to MongoDB from your local machine or any environment during development.</p>
<p>To allow your application to connect to the database:</p>
<ul>
<li><p>Go to the "Network Access" section in the Security sidebar of your MongoDB dashboard.</p>
</li>
<li><p>Click on “ADD IP ADDRESS.”</p>
</li>
<li><p>Choose “Allow Access from Anywhere”, then click on Confirm.</p>
</li>
</ul>
<p>At this point, you can set up your <code>server.js</code></p>
<pre><code class="lang-javascript"><span class="hljs-comment">//server.js</span>
<span class="hljs-built_in">require</span>(<span class="hljs-string">"dotenv"</span>).config();
<span class="hljs-keyword">const</span> mongoose = <span class="hljs-built_in">require</span>(<span class="hljs-string">"mongoose"</span>);
<span class="hljs-keyword">const</span> dotenv = <span class="hljs-built_in">require</span>(<span class="hljs-string">"dotenv"</span>); <span class="hljs-comment">//to Manage our environment variable</span>

dotenv.config({ <span class="hljs-attr">path</span>: <span class="hljs-string">"./config.env"</span> });
<span class="hljs-comment">// console.log(process.env.NODE_ENV);</span>

<span class="hljs-keyword">const</span> app = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./app"</span>);

<span class="hljs-keyword">const</span> db = process.env.DB;
<span class="hljs-comment">//connect the application to database using MongoDB</span>

mongoose
  .connect(db)
  .then(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"DB connection Successful"</span>);
  })
  .catch(<span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(err);
  });

<span class="hljs-keyword">const</span> port = process.env.PORT || <span class="hljs-number">3000</span>;
<span class="hljs-comment">// console.log(process.env.PORT)</span>

app.listen(port, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`App running on port <span class="hljs-subst">${port}</span>`</span>);
});
</code></pre>
<p>The <code>server.js</code> file is responsible for handling all server-related functions and logic. From the code above, the <code>server.js</code> file loads the environment variables using <code>dotenv</code>, connects your backend to MongoDB using <code>mongoose</code>, and starts the Express server. It gets the database URL and port from the <code>config.env</code> file, connects to the database, then runs your application on the specified port (<code>8000</code>).</p>
<p>If the specified port is not found, it falls back to port <code>3000</code> and a confirmation message is printed to the console indicating that the server is up and running on the specified port.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752703203108/a94c724c-ad9c-4653-9081-894ac6e44dd6.png" alt="server-js Telehealth App" class="image--center mx-auto" width="991" height="800" loading="lazy"></p>
<h3 id="heading-connect-the-database-to-mongodb-compass">Connect the Database to MongoDB Compass</h3>
<p>First, download the MongoDB Compass app. (Go here to download and install: <a target="_blank" href="https://www.mongodb.com/try/download/compass">https://www.mongodb.com/try/download/compass</a>). The MongoDB Compass app makes it easy for us to manage our data.</p>
<p>Once the installation is complete, open the app and click on <code>Click to add new connection</code>. Go to your <code>.env</code> file, copy the connection string you initially got when setting up MongoDB, paste it in the URL section, and then click on “connect.” This setup helps you manage your data when you create and delete users.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752703344533/8dff0ff6-66e9-4359-a2c0-fe7a4bd5e4ba.png" alt="Mongo-DB-Compass" class="image--center mx-auto" width="1263" height="790" loading="lazy"></p>
<h3 id="heading-set-up-an-advanced-error-handling-method"><strong>Set up an Advanced Error Handling Method</strong></h3>
<p>You’ll now create an advanced error-handling mechanism. To do so, create a utils folder in your backend, a <code>catchAsync.js</code> file in the utils folder, and add this code:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//catchAsync.js</span>
<span class="hljs-built_in">module</span>.exports = <span class="hljs-function">(<span class="hljs-params">fn</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> <span class="hljs-function">(<span class="hljs-params">req, res, next</span>) =&gt;</span> {
    fn(req, res, next).catch(next);
  };
};
</code></pre>
<p>Next, create an <code>appError.js</code> file still in your utils folder. In the <code>appError.js</code> file, add the following command:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppError</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Error</span> </span>{
  <span class="hljs-keyword">constructor</span>(message, statusCode) {
    <span class="hljs-built_in">super</span>(message);

    <span class="hljs-built_in">this</span>.statusCode = statusCode;
    <span class="hljs-built_in">this</span>.status = <span class="hljs-string">`<span class="hljs-subst">${statusCode}</span>`</span>.startsWith(<span class="hljs-string">"4"</span>) ? <span class="hljs-string">"fail"</span> : <span class="hljs-string">"error"</span>;
    <span class="hljs-built_in">this</span>.isOperational = <span class="hljs-literal">true</span>;

    <span class="hljs-built_in">Error</span>.captureStackTrace(<span class="hljs-built_in">this</span>, <span class="hljs-built_in">this</span>.constructor);
  }
}

<span class="hljs-built_in">module</span>.exports = AppError;
</code></pre>
<p>The code above is helpful in tracking and tracing errors. It also provides you with the URL and file location where your error might be occurring, which helps with cleaner error handling and debugging.</p>
<p>Next, let’s create a global error handler. Start by creating a new folder in the backend directory, and name it “controller”. In the controller folder, create your global error handling file. You can name it anything you like. In this example, it’s called <code>globalErrorHandler.js</code>.</p>
<p>Your <code>globalErrorHandler</code> file will define several functions that handle specific error types, such as database issues, validation failures, or even JWT problems and return a nicely formatted error response for users. For the <code>globalErrorHandler</code> to work properly, you have to create a controller function.</p>
<p>So, next, create an <code>errorController.js</code> file (still inside the controller folder). The <code>errorController.js</code> file responds to the user whenever an error is caught, sending the error in JSON format.</p>
<p><code>globalErrorHandler.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// globalErrorHandler.js</span>
<span class="hljs-keyword">const</span> AppError = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../utils/appError"</span>);

<span class="hljs-keyword">const</span> handleCastErrorDB = <span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> message = <span class="hljs-string">`Invalid <span class="hljs-subst">${err.path}</span>: <span class="hljs-subst">${err.value}</span>.`</span>;
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> AppError(message, <span class="hljs-number">400</span>);
};

<span class="hljs-keyword">const</span> handleDuplicateFieldsDB = <span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> value = err.keyValue ? <span class="hljs-built_in">JSON</span>.stringify(err.keyValue) : <span class="hljs-string">"duplicate field"</span>;
  <span class="hljs-keyword">const</span> message = <span class="hljs-string">`Duplicate field value: <span class="hljs-subst">${value}</span>. Please use another value!`</span>;
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> AppError(message, <span class="hljs-number">400</span>);
};

<span class="hljs-keyword">const</span> handleValidationErrorDB = <span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> errors = <span class="hljs-built_in">Object</span>.values(err.errors).map(<span class="hljs-function">(<span class="hljs-params">el</span>) =&gt;</span> el.message);
  <span class="hljs-keyword">const</span> message = <span class="hljs-string">`Invalid input: <span class="hljs-subst">${errors.join(<span class="hljs-string">". "</span>)}</span>`</span>;
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> AppError(message, <span class="hljs-number">400</span>);
};

<span class="hljs-keyword">const</span> handleJWTError = <span class="hljs-function">() =&gt;</span>
  <span class="hljs-keyword">new</span> AppError(<span class="hljs-string">"Invalid token. Please log in again!"</span>, <span class="hljs-number">401</span>);
<span class="hljs-keyword">const</span> handleJWTExpiredError = <span class="hljs-function">() =&gt;</span>
  <span class="hljs-keyword">new</span> AppError(<span class="hljs-string">"Your token has expired! Please log in again."</span>, <span class="hljs-number">401</span>);

<span class="hljs-built_in">module</span>.exports = {
  handleCastErrorDB,
  handleDuplicateFieldsDB,
  handleValidationErrorDB,
  handleJWTError,
  handleJWTExpiredError,
};
</code></pre>
<p>Here’s what the code above does:</p>
<p>The <code>const handleCastErrorDB = (err) =&gt;..</code> section handles MongoDB CastError which usually happens when an invalid ID is passed, and returns a <code>400 Bad Request</code> error response using your <code>AppError</code> class.</p>
<p>The <code>const handleDuplicateFieldsDB = (err) =&gt;...</code> checks and handles MongoDB duplicate key errors, such as trying to register an email or username that’s already taken and returns a <code>400 Bad Request</code> error.</p>
<p>The <code>const handleValidationErrorDB = (err) =&gt;...</code> handles Mongoose validation errors (like required fields missing or wrong data types). It gathers all the individual validation error messages and combines them into one.</p>
<p>The <code>const handleJWTError = () =&gt;</code> and <code>const handleJWTExpiredError = () =&gt;</code> handle errors which might occur as a result of invalid, tampered, or expired JWT tokens and return a <code>401 Unauthorized</code> error response.</p>
<p>The <code>module.exports = {……};</code> section exports all the individual error handlers so they can be used in the <code>errorController.js</code> file.</p>
<p><code>errorController.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//errorController.js</span>
<span class="hljs-keyword">const</span> errorHandlers = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./globalErrorHandler"</span>);

<span class="hljs-keyword">const</span> {
  handleCastErrorDB,
  handleDuplicateFieldsDB,
  handleValidationErrorDB,
  handleJWTError,
  handleJWTExpiredError,
} = errorHandlers;

<span class="hljs-built_in">module</span>.exports = <span class="hljs-function">(<span class="hljs-params">err, req, res, next</span>) =&gt;</span> {
  err.statusCode = err.statusCode || <span class="hljs-number">500</span>;
  err.status = err.status || <span class="hljs-string">"error"</span>;

  <span class="hljs-keyword">let</span> error = { ...err, <span class="hljs-attr">message</span>: err.message };

  <span class="hljs-keyword">if</span> (err.name === <span class="hljs-string">"CastError"</span>) error = handleCastErrorDB(err);
  <span class="hljs-keyword">if</span> (err.code === <span class="hljs-number">11000</span>) error = handleDuplicateFieldsDB(err);
  <span class="hljs-keyword">if</span> (err.name === <span class="hljs-string">"ValidationError"</span>) error = handleValidationErrorDB(err);
  <span class="hljs-keyword">if</span> (err.name === <span class="hljs-string">"JsonWebTokenError"</span>) error = handleJWTError();
  <span class="hljs-keyword">if</span> (err.name === <span class="hljs-string">"TokenExpiredError"</span>) error = handleJWTExpiredError();

  res.status(error.statusCode).json({
    <span class="hljs-attr">status</span>: error.status,
    <span class="hljs-attr">message</span>: error.message,
    ...(process.env.NODE_ENV === <span class="hljs-string">"production"</span> &amp;&amp; { error, <span class="hljs-attr">stack</span>: err.stack }),
  });
};
</code></pre>
<p>To ensure your error-handling function works properly, head to your <code>App.js</code> and add the command:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//import command</span>
<span class="hljs-keyword">const</span> globalErrorHandler = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./controller/errorController"</span>);
<span class="hljs-keyword">const</span> AppError = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./utils/appError"</span>);

<span class="hljs-comment">//Catch unknown routes</span>
app.all(<span class="hljs-string">"/{*any}"</span>, <span class="hljs-function">(<span class="hljs-params">req, res, next</span>) =&gt;</span> {
  next(<span class="hljs-keyword">new</span> AppError(<span class="hljs-string">`Can't find <span class="hljs-subst">${req.originalUrl}</span> on this server!`</span>, <span class="hljs-number">404</span>)); });

app.use(globalErrorHandler);
</code></pre>
<p>This ensures that all errors are properly handled and sends the error response to the user.</p>
<h3 id="heading-create-user-model"><strong>Create User Model</strong></h3>
<p>To build a user model, create a new folder in the backend directory and name it “model.” Inside the model folder, create a new file named <code>userModel.js</code>.</p>
<p>The <code>userModel.js</code> file is built essentially for user authentication and security. It provides a validation-rich schema for managing users using Mongoose, which maps how user data is structured in the MongoDB database. It includes validations, password hashing, and methods to compare user passwords securely.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//userModel.js</span>
<span class="hljs-keyword">const</span> mongoose = <span class="hljs-built_in">require</span>(<span class="hljs-string">"mongoose"</span>);
<span class="hljs-keyword">const</span> validator = <span class="hljs-built_in">require</span>(<span class="hljs-string">"validator"</span>);
<span class="hljs-keyword">const</span> bcrypt = <span class="hljs-built_in">require</span>(<span class="hljs-string">"bcryptjs"</span>);

<span class="hljs-keyword">const</span> userSchema = <span class="hljs-keyword">new</span> mongoose.Schema(
  {
    <span class="hljs-attr">username</span>: {<span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>, <span class="hljs-attr">required</span>: [<span class="hljs-literal">true</span>, <span class="hljs-string">"Please provide username"</span>], <span class="hljs-attr">trim</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">minlength</span>: <span class="hljs-number">3</span>, <span class="hljs-attr">maxlength</span>: <span class="hljs-number">30</span>, <span class="hljs-attr">index</span>: <span class="hljs-literal">true</span>,},
    <span class="hljs-attr">email</span>: {<span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>, <span class="hljs-attr">required</span>: [<span class="hljs-literal">true</span>, <span class="hljs-string">"Please Provide an email"</span>], <span class="hljs-attr">unique</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">lowercase</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">validate</span>: [validator.isEmail, <span class="hljs-string">"Please provide a valid email"</span>],},
    <span class="hljs-attr">password</span>: {<span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>, <span class="hljs-attr">required</span>: [<span class="hljs-literal">true</span>, <span class="hljs-string">"Please provide a Password"</span>], <span class="hljs-attr">minlength</span>: <span class="hljs-number">8</span>, <span class="hljs-attr">select</span>: <span class="hljs-literal">false</span>,},
    <span class="hljs-attr">passwordConfirm</span>: {<span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>, <span class="hljs-attr">required</span>: [<span class="hljs-literal">true</span>, <span class="hljs-string">"Please confirm your Password"</span>],
     <span class="hljs-attr">validate</span>: {<span class="hljs-attr">validator</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">el</span>) </span>{<span class="hljs-keyword">return</span> el === <span class="hljs-built_in">this</span>.password;},
        <span class="hljs-attr">message</span>: <span class="hljs-string">"Passwords do not match"</span>,
      },
    },
    <span class="hljs-attr">isVerified</span>: {<span class="hljs-attr">type</span>: <span class="hljs-built_in">Boolean</span>, <span class="hljs-attr">default</span>: <span class="hljs-literal">false</span>,}, <span class="hljs-attr">otp</span>: <span class="hljs-built_in">String</span>,
    <span class="hljs-attr">otpExpires</span>: <span class="hljs-built_in">Date</span>,
     <span class="hljs-attr">resetPasswordOTP</span>: <span class="hljs-built_in">String</span>,
      <span class="hljs-attr">resetPasswordOTPExpires</span>: <span class="hljs-built_in">Date</span>,
    <span class="hljs-attr">createdAt</span>: {<span class="hljs-attr">type</span>: <span class="hljs-built_in">Date</span>, <span class="hljs-attr">default</span>: <span class="hljs-built_in">Date</span>.now,},}, { <span class="hljs-attr">timestamps</span>: <span class="hljs-literal">true</span> });

<span class="hljs-comment">// Hash password before saving</span>
userSchema.pre(<span class="hljs-string">"save"</span>, <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">next</span>) </span>{
  <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.isModified(<span class="hljs-string">"password"</span>)) <span class="hljs-keyword">return</span> next();

  <span class="hljs-built_in">this</span>.password = <span class="hljs-keyword">await</span> bcrypt.hash(<span class="hljs-built_in">this</span>.password, <span class="hljs-number">12</span>);
  <span class="hljs-built_in">this</span>.passwordConfirm = <span class="hljs-literal">undefined</span>; <span class="hljs-comment">// Remove passwordConfirm before saving</span>
  next();
});

<span class="hljs-keyword">const</span> User = mongoose.model(<span class="hljs-string">"User"</span>, userSchema);
<span class="hljs-built_in">module</span>.exports = User;
</code></pre>
<h3 id="heading-auth-controller"><strong>Auth Controller</strong></h3>
<p>Now, you can create logic that regulates your user's authentication process. This authentication logic consists of the sign-up, sign-in (log-in), OTP, and so on. </p>
<p>To do so, first head to your controller folder and create a new file. Call it <code>authController.js</code> because it handles the authentication flow of your project.</p>
<p>After you’ve created the file, you’ll create your sign-up function.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//import</span>
<span class="hljs-keyword">const</span> User = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../model/userModel"</span>);
<span class="hljs-keyword">const</span> AppError = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../utils/appError"</span>);
<span class="hljs-keyword">const</span> catchAsync = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../utils/catchAsync"</span>);
<span class="hljs-keyword">const</span> generateOtp = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../utils/generateOtp"</span>);
<span class="hljs-keyword">const</span> jwt = <span class="hljs-built_in">require</span>(<span class="hljs-string">"jsonwebtoken"</span>);
<span class="hljs-keyword">const</span> sendEmail = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../utils/email"</span>)

<span class="hljs-built_in">exports</span>.signup = catchAsync(<span class="hljs-keyword">async</span> (req, res, next) =&gt; {
  <span class="hljs-keyword">const</span> { email, password, passwordConfirm, username } = req.body;

  <span class="hljs-keyword">const</span> existingUser = <span class="hljs-keyword">await</span> User.findOne({ email });

  <span class="hljs-keyword">if</span> (existingUser) <span class="hljs-keyword">return</span> next(<span class="hljs-keyword">new</span> AppError(<span class="hljs-string">"Email already registered"</span>, <span class="hljs-number">400</span>));

  <span class="hljs-keyword">const</span> otp = generateOtp();

  <span class="hljs-keyword">const</span> otpExpires = <span class="hljs-built_in">Date</span>.now() + <span class="hljs-number">24</span> <span class="hljs-number">60</span> <span class="hljs-number">60</span> * <span class="hljs-number">1000</span>; <span class="hljs-comment">//when thhe otp will expire (1 day)</span>

  <span class="hljs-keyword">const</span> newUser = <span class="hljs-keyword">await</span> User.create({
    username,
    email,
    password,
    passwordConfirm,
    otp,
    otpExpires,
  });

  <span class="hljs-comment">//configure email sending functionality</span>
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">await</span> sendEmail({
      <span class="hljs-attr">email</span>: newUser.email,
      <span class="hljs-attr">subject</span>: <span class="hljs-string">"OTP for email Verification"</span>,
      <span class="hljs-attr">html</span>: <span class="hljs-string">`&lt;h1&gt;Your OTP is : <span class="hljs-subst">${otp}</span>&lt;/h1&gt;`</span>,
    });

    createSendToken(newUser, <span class="hljs-number">200</span>, res, <span class="hljs-string">"Registration successful"</span>);
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Email send error:"</span>, error);
    <span class="hljs-keyword">await</span> User.findByIdAndDelete(newUser.id);
    <span class="hljs-keyword">return</span> next(
      <span class="hljs-keyword">new</span> AppError(<span class="hljs-string">"There is an error sending the email. Try again"</span>, <span class="hljs-number">500</span>)
    );
  }
});
</code></pre>
<p><code>const { email, password, passwordConfirm, username } = req.body;</code> extracts the necessary registration details from the incoming request: email, password, password confirmation, and username during user sign-up.</p>
<p><code>const existingUser = await User.findOne({ email });</code> checks the database to see if a user already exists with the given email. If yes, it sends an error response using your <code>AppError</code> utility.</p>
<p><code>const otp = generateOtp();</code> generates the OTP, and <code>const otpExpires =</code> <a target="_blank" href="http://date.now"><code>Date.now</code></a><code>()…..</code> is used to set the OTP to expire at a specified time or day.</p>
<p>With <code>const newUser = await User.create({…});</code>, the new user is saved in MongoDB with their credentials and the OTP info, with the password automatically hashed.</p>
<p><code>await sendEmail({…});</code> sends an email to the user. This email contains their sign-in OTP. If the email is sent successfully, <code>createSendToken(newUser, 200, res, "Registration successful");</code> (which is a utility function) generates a JWT token and sends it in the response with a success message.</p>
<p>If the email fails to send or something goes wrong, <code>await User.findByIdAndDelete(</code><a target="_blank" href="http://newuser.id"><code>newUser.id</code></a><code>);</code> deletes the user from the database to keep things clean, and an error message of <code>There is an error sending the email. Try again", 500</code> is returned.</p>
<h3 id="heading-generate-otp"><strong>Generate OTP</strong></h3>
<p>To ensure that your users' OTP is successfully sent to them, in the utils folder, create a new file and name it <code>generateOtp.js</code>. Then add the code:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">module</span>.exports = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.floor(<span class="hljs-number">1000</span> + <span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">9000</span>).toString();
};
</code></pre>
<p>The code above is a function that generates a user's random 4-digit OTP and returns it as a string.</p>
<p>After completing the code above, go to your authController.js and ensure you import the <code>generateOtp.js</code> in the import section.</p>
<h3 id="heading-create-user-token"><strong>Create User Token</strong></h3>
<p>Next, the user sign-in token will be created, and it will be assigned to the user upon sign-in.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> signToken = <span class="hljs-function">(<span class="hljs-params">userId</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> jwt.sign({ <span class="hljs-attr">id</span>: userId }, process.env.JWT_SECRET, {
    <span class="hljs-attr">expiresIn</span>: process.env.JWT_EXPIRES_IN || <span class="hljs-string">"90d"</span>,
  });
};

<span class="hljs-comment">//function to create the token</span>
<span class="hljs-keyword">const</span> createSendToken = <span class="hljs-function">(<span class="hljs-params">user, statusCode, res, message</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> token = signToken(user._id);

  <span class="hljs-comment">//function to generate the cookie</span>
  <span class="hljs-keyword">const</span> cookieOptions = {
    <span class="hljs-attr">expires</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(
      <span class="hljs-built_in">Date</span>.now() + process.env.JWT_COOKIE_EXPIRES_IN <span class="hljs-number">24</span> <span class="hljs-number">60</span> <span class="hljs-number">60</span> <span class="hljs-number">1000</span>
    ),

    <span class="hljs-attr">httponly</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">secure</span>: process.env.NODE_ENV === <span class="hljs-string">"production"</span>, <span class="hljs-comment">//only secure in production</span>
    <span class="hljs-attr">sameSite</span>: process.env.NODE_ENV === <span class="hljs-string">"production"</span> ? <span class="hljs-string">"none"</span> : <span class="hljs-string">"Lax"</span>,
  };

  res.cookie(<span class="hljs-string">"token"</span>, token, cookieOptions);

  user.password = <span class="hljs-literal">undefined</span>;
  user.passwordConfirm = <span class="hljs-literal">undefined</span>;
  user.otp = <span class="hljs-literal">undefined</span>;
</code></pre>
<p>Before the code above can work perfectly, create a JWT in your <code>.env</code> file.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//.env</span>
JWT_SECRET = kaklsdolrnnhjfsnlsoijfbwhjsioennbandksd;
JWT_EXPIRES_IN = <span class="hljs-number">90</span>d
JWT_COOKIE_EXPIRES_IN = <span class="hljs-number">90</span>
</code></pre>
<p>The code above is how the <code>.env</code> file should look. Your <code>JWT_SECRET</code> can be anything, just as you can see in the code.</p>
<p>Note: The user token creation logic should run before the sign-in logic. So in that case, the <code>signToken</code> and <code>createSendToken</code> logic should be placed at the top before the <code>signup</code> logic.</p>
<h3 id="heading-send-email"><strong>Send Email</strong></h3>
<p>Next, you need to configure your email sending functionality so you can automatically send the user's OTP to their email whenever they sign in. To configure the email, head to the utils folder, create a new file, and give it a name. In this example, the name is <code>email.js</code>.</p>
<p>In <code>email.js,</code> we will send emails using the <code>nodemailer</code> package and Gmail as a provider.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//email.js</span>
<span class="hljs-keyword">const</span> nodemailer = <span class="hljs-built_in">require</span>(<span class="hljs-string">'nodemailer'</span>);

<span class="hljs-keyword">const</span> sendEmail = <span class="hljs-keyword">async</span> (options) =&gt; {
  <span class="hljs-keyword">const</span> transporter = nodemailer.createTransport({
    <span class="hljs-attr">service</span>: <span class="hljs-string">'Gmail'</span>,
    <span class="hljs-attr">auth</span>: {
      <span class="hljs-attr">user</span>: process.env.HOST_EMAIL,
      <span class="hljs-attr">pass</span>: process.env.EMAIL_PASS
    }
  })

  <span class="hljs-comment">//defining email option and structure</span>

  <span class="hljs-keyword">const</span> mailOptions = {
    <span class="hljs-attr">from</span>: <span class="hljs-string">`"{HOST Name}" &lt;{HOST Email} &gt;`</span>,
    <span class="hljs-attr">to</span>: options.email,
    <span class="hljs-attr">subject</span>: options.subject,
    <span class="hljs-attr">html</span>: options.html,
  };
  <span class="hljs-keyword">await</span> transporter.sendMail(mailOptions);
};

<span class="hljs-built_in">module</span>.exports = sendEmail;
</code></pre>
<p>From the code above, the <code>const nodemailer = require('nodemailer');</code> command imports the <code>nodemailer</code> package. This is a popular Node.js library for sending emails.</p>
<p>The <code>const transporter = nodemailer.createTransport({…..})</code> is an email transporter. Since we will be using the Gmail service provider, <code>service</code> will be assigned to <code>Gmail</code> and <code>auth</code> pulls your Gmail address and password from the <code>.env</code> file where it’s stored.</p>
<p>Note: The password is not your actual Gmail password but rather your Gmail app password. You can see how you can get your <a target="_blank" href="https://youtu.be/MkLX85XU5rU?si=yBIj4MJDLY7-k-c4">Gmail password here</a>.</p>
<p>Once you’ve successfully gotten your Gmail app password, store it in your <code>.env</code> file.</p>
<h3 id="heading-route-creation"><strong>Route Creation</strong></h3>
<p>At this point, you have finished setting up your project's signup function. Next, you need to test whether your signup works properly using Postman. But before that, let’s set up and define a route where the signup function will be executed.</p>
<p>To set up your route, create a new folder in your backend directory named "routes" and a file named <code>userRouter.js</code>.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>);
<span class="hljs-keyword">const</span> {signup} = <span class="hljs-built_in">require</span>(“../controller/authController”);

<span class="hljs-keyword">const</span> router = express.Router();
router.post(<span class="hljs-string">"/signup"</span>, signup);

<span class="hljs-built_in">module</span>.exports = router;
</code></pre>
<p>Next, go to your <code>App.js</code> file and add the router to it.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> userRouter = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./routes/userRouters"</span>); <span class="hljs-comment">//Route import statement</span>
app.use(<span class="hljs-string">"/api/v1/users"</span>, userRouter) <span class="hljs-comment">//common route for all auth, i.e sign up, log in, forget password, etc.</span>
</code></pre>
<p>After setting up your routes, you can test your signup to see if it works. This is a post request, and the route URL will be <a target="_blank" href="http://localhost:8000/api/v1/users/signup%60"><code>http://localhost:8000/api/v1/users/signup</code></a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752704061383/380d8480-9997-4678-8ca7-0ed86ea24481.png" alt="New user Sign up Testing" class="image--center mx-auto" width="1280" height="716" loading="lazy"></p>
<p>The image above shows that the signup function works perfectly with a <code>statusCode</code> of <code>200</code> and an OTP code being sent to the user’s email.</p>
<p>Congratulations on reaching this point! You can check your MongoDB database to see if the user is displayed there.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752703565468/0f23f8ab-d17e-4555-8347-475bb6483b8a.png" alt="Mongo-DB-Compass-User-Display" class="image--center mx-auto" width="1280" height="495" loading="lazy"></p>
<p>From the image above, you can see that the user details are obtained and the password is in an encrypted form, which ensures the user credentials are safe.</p>
<h3 id="heading-create-a-verify-account-controller-function"><strong>Create a Verify Account Controller Function</strong></h3>
<p>In this section, you’ll create a Verify Account controller function. This function verifies a user’s account using the OTP code sent to their email address.</p>
<p>First, go to your <code>authController.js</code> file and add:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">exports</span>.verifyAccount = catchAsync(<span class="hljs-keyword">async</span> (req, res, next) =&gt; {
  <span class="hljs-keyword">const</span> { email, otp } = req.body;

  <span class="hljs-keyword">if</span> (!email || !otp) {
    <span class="hljs-keyword">return</span> next(<span class="hljs-keyword">new</span> AppError(<span class="hljs-string">"Email and OTP are required"</span>, <span class="hljs-number">400</span>));
  }

  <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> User.findOne({ email });

  <span class="hljs-keyword">if</span> (!user) {
    <span class="hljs-keyword">return</span> next(<span class="hljs-keyword">new</span> AppError(<span class="hljs-string">"No user found with this email"</span>, <span class="hljs-number">404</span>));
  }

  <span class="hljs-keyword">if</span> (user.otp !== otp) {
    <span class="hljs-keyword">return</span> next(<span class="hljs-keyword">new</span> AppError(<span class="hljs-string">"Invalid OTP"</span>, <span class="hljs-number">400</span>));
  }

  <span class="hljs-keyword">if</span> (<span class="hljs-built_in">Date</span>.now() &gt; user.otpExpires) {
    <span class="hljs-keyword">return</span> next(
      <span class="hljs-keyword">new</span> AppError(<span class="hljs-string">"OTP has expired. Please request a new OTP."</span>, <span class="hljs-number">400</span>)
    );
  }

  user.isVerified = <span class="hljs-literal">true</span>;
  user.otp = <span class="hljs-literal">undefined</span>;
  user.otpExpires = <span class="hljs-literal">undefined</span>;

  <span class="hljs-keyword">await</span> user.save({ <span class="hljs-attr">validateBeforeSave</span>: <span class="hljs-literal">false</span> });

  <span class="hljs-comment">// ✅ Optionally return a response without logging in</span>
  res.status(<span class="hljs-number">200</span>).json({
    <span class="hljs-attr">status</span>: <span class="hljs-string">"success"</span>,
    <span class="hljs-attr">message</span>: <span class="hljs-string">"Email has been verified"</span>,
  });
});
</code></pre>
<p>Next, create a middleware function to authenticate the currently logged-in user.</p>
<p>In your backend directory, create a new folder called <code>middlewares</code>. Inside the <code>middlewares</code> folder, create a file named <code>isAuthenticated.js</code>.</p>
<p>Add the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//isAuthenticated.js</span>
<span class="hljs-keyword">const</span> jwt = <span class="hljs-built_in">require</span>(<span class="hljs-string">"jsonwebtoken"</span>);
<span class="hljs-keyword">const</span> catchAsync = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../utils/catchAsync"</span>);
<span class="hljs-keyword">const</span> AppError = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../utils/appError"</span>);
<span class="hljs-keyword">const</span> User = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../model/userModel"</span>);

<span class="hljs-keyword">const</span> isAuthenticated = catchAsync(<span class="hljs-keyword">async</span> (req, res, next) =&gt; {
  <span class="hljs-keyword">let</span> token;

  <span class="hljs-comment">// 1. Retrieve token from cookies or Authorization header</span>
  <span class="hljs-keyword">if</span> (req.cookies?.token) {
    token = req.cookies.token;
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (req.headers.authorization?.startsWith(<span class="hljs-string">"Bearer"</span>)) {
    token = req.headers.authorization.split(<span class="hljs-string">" "</span>)[<span class="hljs-number">1</span>];
  }

  <span class="hljs-keyword">if</span> (!token) {
    <span class="hljs-keyword">return</span> next(
      <span class="hljs-keyword">new</span> AppError(
        <span class="hljs-string">"You are not logged in. Please log in to access this resource."</span>,
        <span class="hljs-number">401</span>
      )
    );
  }

  <span class="hljs-comment">// 2. Verify token</span>
  <span class="hljs-keyword">let</span> decoded;
  <span class="hljs-keyword">try</span> {
    decoded = jwt.verify(token, process.env.JWT_SECRET);
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-keyword">return</span> next(
      <span class="hljs-keyword">new</span> AppError(<span class="hljs-string">"Invalid or expired token. Please log in again."</span>, <span class="hljs-number">401</span>)
    );
  }

<span class="hljs-comment">// 3. Confirm user still exists in database</span>
  <span class="hljs-keyword">const</span> currentUser = <span class="hljs-keyword">await</span> User.findById(decoded._id);
  <span class="hljs-keyword">if</span> (!currentUser) {
    <span class="hljs-keyword">return</span> next(
      <span class="hljs-keyword">new</span> AppError(<span class="hljs-string">"User linked to this token no longer exists."</span>, <span class="hljs-number">401</span>)
    );
  }

  <span class="hljs-comment">// 4. Attach user info to request</span>
  req.user = currentUser;
  req.user = {
    <span class="hljs-attr">id</span>: currentUser.id,
    <span class="hljs-attr">name</span>: currentUser.name,
  };

  next();
});

<span class="hljs-built_in">module</span>.exports = isAuthenticated;
<span class="hljs-string">``</span><span class="hljs-string">`
Now, go to your `</span>userRouter.js<span class="hljs-string">` file and add the route for the verify account function:
`</span><span class="hljs-string">``</span>
<span class="hljs-keyword">const</span> { verifyAccount} = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../controller/authController"</span>);
<span class="hljs-keyword">const</span> isAuthenticated = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../middlewares/isAuthenticated"</span>);
router.post(<span class="hljs-string">"/verify"</span>, isAuthenticated, verifyAccount);
</code></pre>
<p>Here is what these two sets of code are doing:</p>
<p>When a user sends a request to the <code>/verify</code> route, the <code>isAuthenticated</code> middleware runs first. It checks whether a valid token exists in the cookie or authorization header. If no token is found, it throws an error: <code>You are not logged in. Please log in to access this resource.</code></p>
<p>If a token is found, it verifies the token and checks if the associated user still exists. If not, another error is thrown: <code>"User linked to this token no longer exists."</code></p>
<p>If the user exists and the token is valid, their details are attached to the request (<code>req.user</code>). The request then proceeds to the <code>verifyAccount</code> controller, which handles OTP verification.</p>
<p>You can test this endpoint using Postman with a POST request to: <a target="_blank" href="http://localhost:8000/api/v1/users/verify%60"><code>http://localhost:8000/api/v1/users/verify</code></a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752703870392/a534d04f-7cb9-4f84-92e1-e9844cfa6921.png" alt="Verify-Account" class="image--center mx-auto" width="1280" height="723" loading="lazy"></p>
<p>The image above shows that the verify token function is working well, and a status code of <code>200</code> is displayed.</p>
<h3 id="heading-login-function"><strong>Login Function</strong></h3>
<p>If you’ve reached this point, you’ve successfully signed up and verified a user’s account. </p>
<p>Now it’s time to create the login function, which allows a verified user to access their account. Here’s how you can do that:</p>
<p>Go to your <code>authController.js</code> file and create your login function by adding the following:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">exports</span>.login = catchAsync(<span class="hljs-keyword">async</span> (req, res, next) =&gt; {
  <span class="hljs-keyword">const</span> { email, password } = req.body;

  <span class="hljs-comment">// 1. Validate email &amp; password presence</span>
  <span class="hljs-keyword">if</span> (!email || !password) {
    <span class="hljs-keyword">return</span> next(<span class="hljs-keyword">new</span> AppError(<span class="hljs-string">"Please provide email and password"</span>, <span class="hljs-number">400</span>));
  }

  <span class="hljs-comment">// 2. Check if user exists and include password</span>
  <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> User.findOne({ email }).select(<span class="hljs-string">"+password"</span>);
  <span class="hljs-keyword">if</span> (!user || !(<span class="hljs-keyword">await</span> user.correctPassword(password, user.password))) {
    <span class="hljs-keyword">return</span> next(<span class="hljs-keyword">new</span> AppError(<span class="hljs-string">"Incorrect email or password"</span>, <span class="hljs-number">401</span>));
  }

  <span class="hljs-comment">// 3. Create JWT token</span>
  <span class="hljs-keyword">const</span> token = signToken(user._id);

  <span class="hljs-comment">// 4. Configure cookie options</span>
  <span class="hljs-keyword">const</span> cookieOptions = {
    <span class="hljs-attr">expires</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(
      <span class="hljs-built_in">Date</span>.now() +
        (<span class="hljs-built_in">parseInt</span>(process.env.JWT_COOKIE_EXPIRES_IN, <span class="hljs-number">10</span>) || <span class="hljs-number">90</span>) <span class="hljs-number">24</span> <span class="hljs-number">60</span> <span class="hljs-number">60</span> <span class="hljs-number">1000</span>
    ),
    <span class="hljs-attr">httpOnly</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-comment">// secure: process.env.NODE_ENV === "production",</span>
    <span class="hljs-comment">// sameSite: process.env.NODE_ENV === "production" ?</span>
    <span class="hljs-comment">//  "None" : "Lax",</span>

    <span class="hljs-comment">//set to false during or for local HTTP and cross-origin</span>
    <span class="hljs-attr">secure</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">sameSite</span>: <span class="hljs-string">"Lax"</span>,
  };

  <span class="hljs-comment">// 5. Send cookie</span>
  res.cookie(<span class="hljs-string">"token"</span>, token, cookieOptions);

});
</code></pre>
<p><code>if (!email || !password) {…}</code> checks if the user actually provided both an email and a password. If not, it returns the error: <code>Please provide email and password", 400</code>.</p>
<p><code>const user = await User.findOne({ email }).select("+password");</code> searches the database for a user with the provided email and explicitly includes the password field, since it’s normally hidden by default in the schema.</p>
<p><code>if (!user || !(await user.correctPassword(…))) {…}</code> checks if the user exists and if the password entered matches the one stored in the database (after hashing comparison). If either is wrong, it throws: <code>Incorrect email or password</code>.</p>
<p>The line <code>signToken(user._id)</code> generates a JWT using the user's unique ID. The <code>cookieOptions</code> object configures how the cookie behaves – it sets the cookie to expire after a specific number of days defined in the <code>.env</code> file, marks it as <code>httpOnly</code> to prevent JavaScript access for security, sets <code>secure</code> to <code>false</code> since the app is currently in development, and uses <code>sameSite: "Lax"</code> to allow cross-origin requests during local testing. </p>
<p>Finally, <code>res.cookie(...)</code> sends the token as a cookie attached to the HTTP response, enabling the client to store the token for authentication purposes.</p>
<p>From the code above, you may have noticed that the password stored in the database is hashed for security reasons. This means it looks completely different from the user's password when logging in. So, even if a user types in the correct password, it won't match the stored hash directly through a simple comparison. </p>
<p>To fix this, you need to compare the entered password with the hashed one using the <code>bcryptjs</code> package.</p>
<p>Head over to your <code>userModel.js</code> file and create a method that handles password comparison. This method will take the plain text password provided by the user and compare it to the hashed password stored in the database.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//userModel.js</span>
<span class="hljs-comment">//create a method responsible for comparing the password stored in the database</span>

userSchema.methods.correctPassword = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">password, userPassword</span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> bcrypt.compare(password, userPassword);
};
</code></pre>
<p>This <code>correctPassword</code> method uses <code>bcrypt.compare()</code>, which internally hashes the plain password and checks if it matches the stored hashed version. This ensures that login validation works correctly and securely, even though the actual password is not stored in plain text.</p>
<p>Next, add the Login functionality to the <code>userRouter.js</code> file.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> {login} = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../controller/authController"</span>);
<span class="hljs-keyword">const</span> isAuthenticated = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../middlewares/isAuthenticated"</span>);

router.post(<span class="hljs-string">"/login"</span>, login);
</code></pre>
<p>You can test this endpoint using Postman with a <code>POST</code> request to: <a target="_blank" href="http://localhost:8000/api/v1/users/login%60"><code>http://localhost:8000/api/v1/users/login</code></a></p>
<h3 id="heading-logout-function"><strong>Logout Function</strong></h3>
<p>At this point, you can implement the logout function to end a user's session securely. To do this, navigate to your <code>authController.js</code> file and add the following function:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//creating a log out function</span>
<span class="hljs-built_in">exports</span>.logout = catchAsync(<span class="hljs-keyword">async</span> (req, res, next) =&gt; {
  res.cookie(<span class="hljs-string">"token"</span>, <span class="hljs-string">"loggedout"</span>, {
    <span class="hljs-attr">expires</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(<span class="hljs-number">0</span>),
    <span class="hljs-attr">httpOnly</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">secure</span>: process.env.NODE_ENV === <span class="hljs-string">"production"</span>,
  });

  res.status(<span class="hljs-number">200</span>).json({
    <span class="hljs-attr">status</span>: <span class="hljs-string">"success"</span>,
    <span class="hljs-attr">message</span>: <span class="hljs-string">"Logged out successfully"</span>,
  });
});
</code></pre>
<p>This function works by overwriting the authentication cookie named <code>token</code> with the value <code>"loggedout"</code> and setting its expiration time to the past using <code>new Date(0)</code>. This effectively invalidates the cookie and removes it from the browser. </p>
<p>The <code>httpOnly: true</code> flag ensures that the cookie cannot be accessed via JavaScript, which protects it from XSS attacks, while the <code>secure</code> flag ensures that the cookie is only sent over HTTPS in a production environment. Once the cookie is cleared, a success response is returned with the message "Logged out successfully" to confirm the action.</p>
<p>Next, add the <code>logout</code> functionality to your route:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> {logout} = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../controller/authController"</span>);
<span class="hljs-keyword">const</span> isAuthenticated = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../middlewares/isAuthenticated"</span>);

router.post(<span class="hljs-string">"/logout"</span>, logout);
</code></pre>
<p>Then, head to Postman to test your logout function and see if it works.</p>
<h2 id="heading-frontend-setup"><strong>Frontend Setup</strong></h2>
<p>Now that your backend is up and running, you can integrate it into your frontend application.</p>
<p>First, navigate to the frontend directory using the command <code>cd Frontend</code>.</p>
<p>Create a new folder in the <code>src</code> folder where your authentication-related files will live. Depending on your preference or app structure, you can name it something like <code>auth</code> or <code>pages</code>. Then, create a new file called <code>NewUser. js</code>. This file will handle user signup functionality.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">'axios'</span>;
<span class="hljs-keyword">import</span> React, { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { Link, useNavigate } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-router-dom'</span>;
<span class="hljs-keyword">import</span> { Loader } <span class="hljs-keyword">from</span> <span class="hljs-string">'lucide-react'</span>;
<span class="hljs-keyword">import</span> { useDispatch } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-redux'</span>;
<span class="hljs-keyword">import</span> { setAuthUser, setPendingEmail } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../../../store/authSlice'</span>;

<span class="hljs-keyword">const</span> API_URL = <span class="hljs-keyword">import</span>.meta.env.VITE_API_URL;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">NewUser</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> dispatch = useDispatch();
  <span class="hljs-keyword">const</span> navigate = useNavigate();

  <span class="hljs-keyword">const</span> [loading, setLoading] = useState(<span class="hljs-literal">false</span>);

  <span class="hljs-keyword">const</span> [formData, setFormData] = useState({
    <span class="hljs-attr">username</span>: <span class="hljs-string">''</span>,
    <span class="hljs-attr">email</span>: <span class="hljs-string">''</span>,
    <span class="hljs-attr">password</span>: <span class="hljs-string">''</span>,
    <span class="hljs-attr">passwordConfirm</span>: <span class="hljs-string">''</span>,
  });

  <span class="hljs-keyword">const</span> handleChange = <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> { name, value } = e.target;
    setFormData(<span class="hljs-function">(<span class="hljs-params">prev</span>) =&gt;</span> ({ ...prev, [name]: value }));
  };

  <span class="hljs-keyword">const</span> submitHandler = <span class="hljs-keyword">async</span> (e) =&gt; {
    e.preventDefault();
    setLoading(<span class="hljs-literal">true</span>);
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axios.post(<span class="hljs-string">`<span class="hljs-subst">${API_URL}</span>/users/signup`</span>, formData, {
        <span class="hljs-attr">withCredentials</span>: <span class="hljs-literal">true</span>,
      });
      <span class="hljs-keyword">const</span> user = response.data.data.user;
      dispatch(setAuthUser(user));
      dispatch(setPendingEmail(formData.email)); <span class="hljs-comment">// Save email for OTP</span>
      navigate(<span class="hljs-string">'/verifyAcct'</span>); <span class="hljs-comment">// Navigate to OTP verification page</span>
    } <span class="hljs-keyword">catch</span> (error) {
      alert(error.response?.data?.message || <span class="hljs-string">'Signup failed'</span>);
    } <span class="hljs-keyword">finally</span> {
      setLoading(<span class="hljs-literal">false</span>);
    }
  };

  <span class="hljs-keyword">return</span> (
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
// visit the frontend Github repository to see the remaining code for the OTP Verification

https://github.com/Derekvibe/Telehealth_Frontend/blob/main/src/pages/Auth/Join/NewUser.jsx
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> NewUser;
</code></pre>
<p>The code above renders a signup form with fields for <code>username</code>, <code>email</code>, <code>password</code> and <code>passwordConfirm</code>. When the user submits the form, the frontend sends a <code>POST</code> request to the backend’s <code>/users/signup</code> endpoint using <code>Axios</code>. The <code>withCredentials: true</code> option ensures cookies like the <code>auth token</code> are properly set by the backend.</p>
<p>If the signup is successful, the user data is dispatched into Redux using <code>setAuthUser()</code>, and their email is saved with <code>setPendingEmail()</code> so it can be used during <code>OTP</code> verification. Then, the user is redirected to the <code>/verifyAcct</code> route, where they can enter their <code>OTP</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752704266192/0d1d5891-000a-48dc-a1d8-306a0103824a.png" alt="Frontend-Sign-Up" class="image--center mx-auto" width="815" height="675" loading="lazy"></p>
<h3 id="heading-otp-verification-page"><strong>OTP Verification Page</strong></h3>
<p>The OTP verification page is the next step in the user authentication process. Once a user signs up, they are redirected to enter the 4-digit OTP sent to their email. This verifies their account before allowing login access.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React, { useState, useRef, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { useSelector, useDispatch } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-redux'</span>;
<span class="hljs-keyword">import</span> { useNavigate, Link } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-router-dom'</span>;
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">'axios'</span>;
<span class="hljs-keyword">import</span> { clearPendingEmail } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../../../store/authSlice'</span>;

<span class="hljs-keyword">const</span> API_URL = <span class="hljs-keyword">import</span>.meta.env.VITE_API_URL || <span class="hljs-string">'http://localhost:5000/api'</span>; <span class="hljs-comment">// adjust as needed</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">VerifyAcct</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [code, setCode] = useState([<span class="hljs-string">''</span>, <span class="hljs-string">''</span>, <span class="hljs-string">''</span>, <span class="hljs-string">''</span>]);
  <span class="hljs-keyword">const</span> [loading, setLoading] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">const</span> [resendLoading, setResendLoading] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">const</span> [timer, setTimer] = useState(<span class="hljs-number">60</span>);

  <span class="hljs-keyword">const</span> inputsRef = useRef([]);
  <span class="hljs-keyword">const</span> dispatch = useDispatch();
  <span class="hljs-keyword">const</span> navigate = useNavigate();
  <span class="hljs-keyword">const</span> email = useSelector(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> state.auth.pendingEmail);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">let</span> interval;
    <span class="hljs-keyword">if</span> (timer &gt; <span class="hljs-number">0</span>) {
      interval = <span class="hljs-built_in">setInterval</span>(<span class="hljs-function">() =&gt;</span> setTimer(<span class="hljs-function">(<span class="hljs-params">prev</span>) =&gt;</span> prev - <span class="hljs-number">1</span>), <span class="hljs-number">1000</span>);
    }
    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">clearInterval</span>(interval);
  }, [timer]);

  <span class="hljs-keyword">const</span> handleChange = <span class="hljs-function">(<span class="hljs-params">value, index</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (!<span class="hljs-regexp">/^\d*$/</span>.test(value)) <span class="hljs-keyword">return</span>;
    <span class="hljs-keyword">const</span> newCode = [...code];
<span class="hljs-comment">// visit the frontend Github repository to see the remaining code for the OTP Verification</span>
https:<span class="hljs-comment">//github.com/Derekvibe/Telehealth_Frontend/blob/main/src/pages/Auth/login/VerifyAcct.jsx</span>
}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> VerifyAcct;
</code></pre>
<p>Here’s what the code does: </p>
<p>The OTP is stored as an array of 4 characters (<code>[‘ ‘, ‘ ‘, ‘ ‘, ‘ ‘]</code>). Each box only accepts digits, and focus automatically moves to the next input as the user types in the digit. The focus returns to the previous input box if the user presses the backspace button on an empty box.</p>
<p>When the OTP has been added and the form is submitted, the 4-digit code is joined into a string and an <code>HTTP POST</code> request is made to the backend <code>/user/verify/</code> endpoint along with the stored email and OTP. If the verification is successful, the user is alerted and redirected to the login page, and if not, an error is shown.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752704448954/8ea46e32-c6d9-42e1-a016-f04b259eb0e7.png" alt="Frontend-OTP" class="image--center mx-auto" width="798" height="631" loading="lazy"></p>
<h3 id="heading-log-in"><strong>Log In</strong></h3>
<p>Now you can create the login interface for your application. First, create a <code>Login.jsx</code> file and input the code:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//Login.Jsx</span>

<span class="hljs-keyword">import</span> React, { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { Link, useNavigate } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-router-dom'</span>;
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">'axios'</span>;

<span class="hljs-keyword">const</span> API_URL = <span class="hljs-keyword">import</span>.meta.env.VITE_API_URL || <span class="hljs-string">'https://telehealth-backend-2m1f.onrender.com/api/v1'</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Join</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [formData, setFormData] = useState({ <span class="hljs-attr">email</span>: <span class="hljs-string">''</span>, <span class="hljs-attr">password</span>: <span class="hljs-string">''</span> });
  <span class="hljs-keyword">const</span> [loading, setLoading] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">const</span> [error, setError] = useState(<span class="hljs-string">''</span>);
  <span class="hljs-keyword">const</span> navigate = useNavigate();

  <span class="hljs-keyword">const</span> handleChange = <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    setFormData({ ...formData, [e.target.name]: e.target.value });
  };

  <span class="hljs-keyword">const</span> handleLogin = <span class="hljs-keyword">async</span> (e) =&gt; {
    e.preventDefault();
    setLoading(<span class="hljs-literal">true</span>);
    setError(<span class="hljs-string">''</span>);

    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> axios.post(<span class="hljs-string">`<span class="hljs-subst">${API_URL}</span>/users/login`</span>, formData, {
        <span class="hljs-attr">withCredentials</span>: <span class="hljs-literal">true</span>,
      });

      <span class="hljs-keyword">if</span> (res.data.status === <span class="hljs-string">'success'</span>) {
        <span class="hljs-keyword">const</span> { token, user, streamToken } = res.data;

        <span class="hljs-comment">// Save to localStorage</span>
        <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'authToken'</span>, token);
        <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'user'</span>, <span class="hljs-built_in">JSON</span>.stringify(user));
        <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'streamToken'</span>, streamToken);

        navigate(<span class="hljs-string">'/dashboard'</span>);
      }
    } <span class="hljs-keyword">catch</span> (err) {
      <span class="hljs-built_in">console</span>.error(err);
      setError(
        err.response?.data?.message || <span class="hljs-string">'Something went wrong. Please try again.'</span>
      );
    } <span class="hljs-keyword">finally</span> {
      setLoading(<span class="hljs-literal">false</span>);
    }
  };

  <span class="hljs-keyword">return</span> (
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
{// visit the frontend Github repository to see the remaining code for the OTP Verification
https://github.com/Derekvibe/Telehealth_Frontend/blob/main/src/pages/Auth/login/Login.jsx
 <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
);
}
</code></pre>
<p>The <code>Export default Join;</code> component allows a registered and verified user to log into your application using their email and password. It handles form submission, talks to the backend, and securely stores user data if login is successful. </p>
<p><code>handleChange()</code> updates the email or password field as the user types. </p>
<p><code>handleLogin()</code> is triggered when the login form is submitted. When the login button is triggered, it sends a <code>Post</code> request to <code>/users/login</code> with the form data, which includes <code>{withCredentials: true}</code> to enable cookie handling. </p>
<p>If login is successful, it extracts the JWT token, user data, and Stream Chat token from the response and stores them in the <code>localStorage</code> so the user stays logged in across sessions. Then it redirects the user to the dashboard page using <code>navigate(‘/dashboard’)</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752704515845/55c6e74a-a8bc-462a-b988-67b0e8df40ac.png" alt="Frontend-Login" class="image--center mx-auto" width="859" height="583" loading="lazy"></p>
<h3 id="heading-set-up-the-frontend-route"><strong>Set Up the Frontend Route</strong></h3>
<p>Just as you set up the backend route, you have to do the same for the frontend.</p>
<p>Head to <code>App.jsx</code>. Before adding the route, make sure you have installed the <code>react-router-dom</code> package. If not, run this command in the frontend terminal: </p>
<p><code>npm install react-router-dom</code></p>
<p>Then, add the command to your <code>App.jsx</code> file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { createBrowserRouter, RouterProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-router-dom'</span>;
<span class="hljs-keyword">import</span> HomeIndex <span class="hljs-keyword">from</span> <span class="hljs-string">'./pages/Home/HomeIndex'</span>;
<span class="hljs-keyword">import</span> Hero <span class="hljs-keyword">from</span> <span class="hljs-string">'./pages/Home/Hero'</span>;

<span class="hljs-comment">//Authentication Section</span>
<span class="hljs-keyword">import</span> NewUser <span class="hljs-keyword">from</span> <span class="hljs-string">'./pages/Auth/Join/NewUser'</span>;
<span class="hljs-keyword">import</span> Login <span class="hljs-keyword">from</span> <span class="hljs-string">'./pages/Auth/login/Login'</span>
<span class="hljs-keyword">import</span> VerifyAcct <span class="hljs-keyword">from</span> <span class="hljs-string">'./pages/Auth/login/VerifyAcct'</span>;

<span class="hljs-comment">// Dashboard</span>
<span class="hljs-keyword">import</span> Dashboard <span class="hljs-keyword">from</span> <span class="hljs-string">'./pages/Dashboard/Dashboard'</span>;
<span class="hljs-keyword">import</span> VideoStream <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/VideoStream'</span>;

<span class="hljs-keyword">const</span> router = createBrowserRouter([
  {
    <span class="hljs-attr">path</span>: <span class="hljs-string">'/'</span>,
    <span class="hljs-attr">element</span>: <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">HomeIndex</span> /&gt;</span></span>,
    children: [
      { <span class="hljs-attr">index</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">element</span>: <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Hero</span> /&gt;</span></span> }
    ],
  },

  {
    <span class="hljs-attr">path</span>: <span class="hljs-string">'signup'</span>,
    <span class="hljs-attr">element</span>: <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">NewUser</span> /&gt;</span></span>,
    children: [
      { <span class="hljs-attr">index</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">element</span>: <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">NewUser</span> /&gt;</span></span> }
    ],
  },

  {
    <span class="hljs-attr">path</span>: <span class="hljs-string">'login'</span>,
    <span class="hljs-attr">element</span>: <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Login</span> /&gt;</span></span>,
    children: [
      {<span class="hljs-attr">index</span>:<span class="hljs-literal">true</span>, <span class="hljs-attr">element</span>:<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Login</span> /&gt;</span></span>}
    ]
  },

]);
{<span class="hljs-comment">// visit the frontend Github repository to see the remaining code for the OTP Verification</span>
<span class="hljs-attr">https</span>:<span class="hljs-comment">//github.com/Derekvibe/Telehealth_Frontend/blob/main/src/App.jsx}</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'border border-red-700 w-full min-w-[100vw] min-h-[100vh]'</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">RouterProvider</span> <span class="hljs-attr">router</span>=<span class="hljs-string">{router}</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<h2 id="heading-stream-chat-and-video-integration"><strong>Stream Chat and Video Integration</strong></h2>
<p>Before proceeding to the dashboard, let’s integrate the Stream <a target="_blank" href="https://getstream.io/chat/">Chat</a> and <a target="_blank" href="https://getstream.io/video/">Video</a> functionality into the project.</p>
<p>First, <a target="_blank" href="https://getstream.io/try-for-free/">create a free Stream account</a>, start a new project in your dashboard, and get your <code>APP KEY</code> and <code>API_SECRET</code>.</p>
<pre><code class="lang-javascript">STREAM_API_KEY=your_app_key
STREAM_API_SECRET=your_api_secret
</code></pre>
<p>Watch the Stream <a target="_blank" href="https://youtu.be/kGKq4giL4ok?si=M_nkWAiq4IzGNYD_">Chat React Quick Start Guide</a> to see how you can set it up.</p>
<p>Next, store your Stream <code>APP KEY</code> and <code>API_SECRET</code> in your <code>.env</code>.</p>
<h3 id="heading-install-stream-packages-frontend"><strong>Install Stream Packages (Frontend)</strong></h3>
<p>Now, install the Stream Chat and Video packages in your terminal.</p>
<pre><code class="lang-javascript">npm install stream-chat stream-chat-react
npm install @stream-io/video-react-sdk
npm install @stream-io/stream-chat-css
</code></pre>
<h3 id="heading-stream-token-handler"><strong>Stream Token Handler</strong></h3>
<p>First, create a new file in your frontend Src directory and name it. In this example, it’s <code>StreamContext.jsx</code>. This file sets up a context to fetch and manage the Stream Chat token on login and includes logout functionality.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React, { createContext, useContext, useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;

<span class="hljs-keyword">const</span> API_URL = <span class="hljs-keyword">import</span>.meta.env.VITE_API_URL || <span class="hljs-string">'https://telehealth-backend-2m1f.onrender.com/api/v1'</span>;

<span class="hljs-comment">// 1. Create the context</span>
<span class="hljs-keyword">const</span> StreamContext = createContext();

<span class="hljs-comment">// 2. Provider component</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> StreamProvider = <span class="hljs-function">(<span class="hljs-params">{ children }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> [user, setUser] = useState(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> [token, setToken] = useState(<span class="hljs-literal">null</span>);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> fetchToken = <span class="hljs-keyword">async</span> () =&gt; {
      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> axios.get(<span class="hljs-string">`<span class="hljs-subst">${API_URL}</span>/stream/get-token`</span>, {
          <span class="hljs-attr">withCredentials</span>: <span class="hljs-literal">true</span>,
        });

        <span class="hljs-keyword">if</span> (res.data?.user &amp;&amp; res.data?.token) {
          setUser(res.data.user);
          setToken(res.data.token);
          <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Stream user/token:"</span>, res.data);
        } <span class="hljs-keyword">else</span> {
          <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Token or user missing in response:"</span>, res.data);
        }
      } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error fetching Stream token:"</span>, error);
      }
    };

    fetchToken();
  }, []);

  <span class="hljs-comment">//Log out Functionality</span>
  <span class="hljs-keyword">const</span> logout = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> axios.post(<span class="hljs-string">`<span class="hljs-subst">${API_URL}</span>/users/logout`</span>, {},
        {
          <span class="hljs-attr">withCredentials</span>: <span class="hljs-literal">true</span>
        });

      <span class="hljs-comment">// Clear localStorage</span>
      <span class="hljs-built_in">localStorage</span>.removeItem(<span class="hljs-string">'authToken'</span>);
      <span class="hljs-built_in">localStorage</span>.removeItem(<span class="hljs-string">'user'</span>);
      <span class="hljs-built_in">localStorage</span>.removeItem(<span class="hljs-string">'streamToken'</span>);

      <span class="hljs-comment">// Clear context</span>
      setUser(<span class="hljs-literal">null</span>);
      setToken(<span class="hljs-literal">null</span>);
    } <span class="hljs-keyword">catch</span> (error) {
      <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Logout failed"</span>, error);
    }
  };

  <span class="hljs-comment">// Expose Logout with capital L</span>
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">StreamContext.Provider</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">user</span>, <span class="hljs-attr">token</span>, <span class="hljs-attr">Logout:logout</span> }}&gt;</span>
      {children}
    <span class="hljs-tag">&lt;/<span class="hljs-name">StreamContext.Provider</span>&gt;</span></span>
  );
};

<span class="hljs-comment">// 3. Custom hook for easy access</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> useStream = <span class="hljs-function">() =&gt;</span> useContext(StreamContext);
</code></pre>
<p>The code above creates a StreamContext using React’s Context API. In the <code>useEffect</code> section, it makes a <code>GET</code> request to <code>/stream/get-token</code> to fetch the authenticated user and their Stream token. Then it stores them in <code>user</code> and <code>token</code> states. It also provides the user/token through the context so that any component that needs it can make use of it.</p>
<p>Finally, it adds a <code>Logout</code> method that hits the logout endpoint and clears all stored auth data from <code>localStorage</code>.</p>
<p>Next, open your <code>main.jsx</code> and wrap your entire application with the <code>StreamProvider</code> so all child components can access the Stream context.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// main.jsx</span>
<span class="hljs-keyword">import</span> { createRoot } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-dom/client'</span>;
<span class="hljs-keyword">import</span> { StrictMode } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">'./App'</span>;
<span class="hljs-keyword">import</span> { StreamProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/StreamContext'</span>;

createRoot(<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'root'</span>)).render(
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">StrictMode</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">StreamProvider</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">StreamProvider</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">StrictMode</span>&gt;</span></span>
);
</code></pre>
<h3 id="heading-set-up-stream-api"><strong>Set Up Stream API</strong></h3>
<p>After successfully creating the streamContent, the next step is to set up the Stream API. This will be the endpoint from which the user ID and user Stream token can be generated and fetched during login.</p>
<p>To set it up, navigate to your backend directory by running <code>cd Backend</code> in your terminal. Then install the Stream package using the command:</p>
<pre><code class="lang-javascript">npm install getstream
npm install stream-chat stream-chat-react
</code></pre>
<p>Open your <code>.env</code> file and add your Stream <code>API KEY</code> and <code>API_SECRET</code>:</p>
<pre><code class="lang-javascript">STREAM_API_KEY=your_app_key
STREAM_API_SECRET=your_api_secret
</code></pre>
<p>Next, open your <code>authController.js</code> and create your Stream Chat logic:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//Initialize the Stream Client</span>
<span class="hljs-keyword">const</span> {StreamChat} = <span class="hljs-built_in">require</span>(<span class="hljs-string">"stream-chat"</span>);
<span class="hljs-keyword">const</span> streamClient = StreamChat.getInstance(
  process.env.STREAM_API_KEY,
  process.env.STREAM_API_SECRET
);

<span class="hljs-comment">// Modifies the `createSendToken to include `streamToken`</span>
<span class="hljs-keyword">const</span> createSendToken = <span class="hljs-function">(<span class="hljs-params">user, statusCode, res, message</span>) =&gt;</span> {
……
<span class="hljs-keyword">const</span> streamToken = streamClient.createToken(user._id.toString());

  <span class="hljs-comment">//structure of the cookie response when sent to the user</span>
  res.status(statusCode).json({
    <span class="hljs-attr">status</span>: <span class="hljs-string">"success"</span>,
    message,
    token,
    streamToken,
    <span class="hljs-attr">data</span>: {
      <span class="hljs-attr">user</span>: {
        <span class="hljs-attr">id</span>: user._id.toString(),
        <span class="hljs-attr">name</span>: user.username,
      },
    },
  });
};

<span class="hljs-comment">//login functionality</span>
<span class="hljs-built_in">exports</span>.login = catchAsync(<span class="hljs-keyword">async</span> (req, res, next) =&gt; {
 {…………………..}

<span class="hljs-comment">// Generate Stream token</span>
  <span class="hljs-keyword">await</span> streamClient.upsertUser({
    <span class="hljs-attr">id</span>: user._id.toString(),
    <span class="hljs-attr">name</span>: user.username,
  });
  <span class="hljs-keyword">const</span> streamToken = streamClient.createToken(user._id.toString());

user.password = <span class="hljs-literal">undefined</span>;

  res.status(<span class="hljs-number">200</span>).json({
    <span class="hljs-attr">status</span>: <span class="hljs-string">"success"</span>,
    <span class="hljs-attr">message</span>: <span class="hljs-string">"Login successful"</span>,
    token,
    <span class="hljs-attr">user</span>: {
      <span class="hljs-attr">id</span>: user._id.toString(),
      <span class="hljs-attr">name</span>: user.username,
    },
    streamToken,
  });
</code></pre>
<h3 id="heading-streamroutes-endpoint"><code>streamRoutes</code> <strong>Endpoint</strong></h3>
<p>Next, create an endpoint from which the Stream token can be called. To do this, go to your routes folder and create a new file called <code>streamRoutes.js</code>. In <code>streamRoutes.js</code>, add the command:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>);
<span class="hljs-keyword">const</span> { StreamChat } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"stream-chat"</span>);

<span class="hljs-keyword">const</span> protect = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../middlewares/protect"</span>);

<span class="hljs-keyword">const</span> router = express.Router();

<span class="hljs-keyword">const</span> apiKey = process.env.STREAM_API_KEY;
<span class="hljs-keyword">const</span> apiSecret = process.env.STREAM_API_SECRET;

<span class="hljs-keyword">if</span> (!apiKey || !apiSecret) {
  <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(
    <span class="hljs-string">"Missing Stream credentials. Check your environment variables."</span>
  );
}

<span class="hljs-keyword">const</span> streamClient = StreamChat.getInstance(apiKey, apiSecret);

router.get(<span class="hljs-string">"/get-token"</span>, protect, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> { id, username } = req.user || {};
    <span class="hljs-built_in">console</span>.log(req.user.id, <span class="hljs-string">"User"</span>);
    <span class="hljs-comment">// TRY LOGGING THE ID AND NAME FROM YOUR REQUEST FIRST</span>

    <span class="hljs-keyword">if</span> (!id || !username) {
      <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).json({ <span class="hljs-attr">error</span>: <span class="hljs-string">"Invalid user data"</span> });
    }

    <span class="hljs-comment">// const userId = _id.toString();</span>
    <span class="hljs-keyword">const</span> user = { id, username };

    <span class="hljs-comment">// Ensure user exists in Stream backend</span>
    <span class="hljs-keyword">await</span> streamClient.upsertUser(user);

    <span class="hljs-comment">// Add user to my_general_chat channel</span>
    <span class="hljs-keyword">const</span> channel = streamClient.channel(<span class="hljs-string">"messaging"</span>, <span class="hljs-string">"my_general_chat"</span>);
    <span class="hljs-keyword">await</span> channel.addMembers([id]);


    <span class="hljs-comment">// Generate token</span>
    <span class="hljs-keyword">const</span> token = streamClient.createToken(id);
    res.status(<span class="hljs-number">200</span>).json({ token, user });
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Stream token generation error:"</span>, error);
    res.status(<span class="hljs-number">500</span>).json({ <span class="hljs-attr">error</span>: <span class="hljs-string">"Failed to generate Stream token"</span> });
  }
});

<span class="hljs-comment">/**
 * @route   POST /api/stream/token
 * @desc    Generate a Stream token for any userId from request body (no auth)
 * @access  Public
 */</span>
router.post(<span class="hljs-string">"/token"</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> { userId, name } = req.body;

    <span class="hljs-keyword">if</span> (!userId) {
      <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).json({ <span class="hljs-attr">error</span>: <span class="hljs-string">"userId is required"</span> });
    }

    <span class="hljs-keyword">const</span> userName = name || <span class="hljs-string">"Anonymous"</span>;
    <span class="hljs-keyword">const</span> user = { <span class="hljs-attr">id</span>: userId, <span class="hljs-attr">name</span>: userName };

    <span class="hljs-keyword">await</span> streamClient.upsertUser(user);

    <span class="hljs-comment">// Add user to my_general_chat channel</span>
    <span class="hljs-keyword">const</span> channel = streamClient.channel(<span class="hljs-string">"messaging"</span>, <span class="hljs-string">"my_general_chat"</span>);
    <span class="hljs-keyword">await</span> channel.addMembers([userId]);


    <span class="hljs-keyword">const</span> token = streamClient.createToken(userId);

    res.status(<span class="hljs-number">200</span>).json({
      token,
      <span class="hljs-attr">user</span>: {
        <span class="hljs-attr">id</span>: userId,
        <span class="hljs-attr">name</span>: name,
        <span class="hljs-attr">role</span>: <span class="hljs-string">"admin"</span>,
        <span class="hljs-attr">image</span>: <span class="hljs-string">`https://getstream.io/random_png/?name=<span class="hljs-subst">${name}</span>`</span>,
      },
    });
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Public token generation error:"</span>, error);
    res.status(<span class="hljs-number">500</span>).json({ <span class="hljs-attr">error</span>: <span class="hljs-string">"Failed to generate token"</span> });
  }
});

<span class="hljs-built_in">module</span>.exports = router;
</code></pre>
<h2 id="heading-user-logout-endpoint"><strong>User Logout Endpoint</strong></h2>
<p>Go to your <code>authController.js</code> and create a functionality that handles logging out the user:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">exports</span>.logout = catchAsync(<span class="hljs-keyword">async</span> (req, res, next) =&gt; {
  res.cookie(<span class="hljs-string">"token"</span>, <span class="hljs-string">"loggedout"</span>, {
    <span class="hljs-attr">expires</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(<span class="hljs-number">0</span>),
    <span class="hljs-attr">httpOnly</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">secure</span>: process.env.NODE_ENV === <span class="hljs-string">"production"</span>,
  });

  res.status(<span class="hljs-number">200</span>).json({
    <span class="hljs-attr">status</span>: <span class="hljs-string">"success"</span>,
    <span class="hljs-attr">message</span>: <span class="hljs-string">"Logged out successfully"</span>,
  });
});
</code></pre>
<p>Then register your logout route to your <code>userRouters.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>);
<span class="hljs-keyword">const</span> {logout}= <span class="hljs-built_in">require</span>(<span class="hljs-string">"../controller/authController"</span>);
<span class="hljs-keyword">const</span> isAuthenticated = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../middlewares/isAuthenticated"</span>);


router.post(<span class="hljs-string">"/logout"</span>, isAuthenticated, logout);

<span class="hljs-built_in">module</span>.exports = router;
</code></pre>
<h2 id="heading-chat-and-video-function-frontend">Chat and Video Function (Frontend)</h2>
<p>After setting up your backend Stream API, the last task is setting up chat and video in your frontend application.</p>
<h3 id="heading-dashboardjsx"><code>Dashboard.jsx</code></h3>
<p>Create a new file <code>Dashboard.jsx</code> in your frontend directory. This is where you will set up your Stream and video function.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React, { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;
<span class="hljs-keyword">import</span> {
  Chat,
  Channel,
  ChannelHeader,
  MessageInput,
  MessageList,
  Thread,
  Window,
  useCreateChatClient,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"stream-chat-react"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"stream-chat-react/dist/css/v2/index.css"</span>;
<span class="hljs-keyword">import</span> { useStream } <span class="hljs-keyword">from</span> <span class="hljs-string">"../../components/StreamContext"</span>;
<span class="hljs-keyword">import</span> VideoStream <span class="hljs-keyword">from</span> <span class="hljs-string">"../../components/VideoStream"</span>;
<span class="hljs-keyword">import</span> { useNavigate } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;



<span class="hljs-keyword">const</span> apiKey = <span class="hljs-keyword">import</span>.meta.env.VITE_STREAM_API_KEY;

<span class="hljs-keyword">const</span> API_URL = <span class="hljs-keyword">import</span>.meta.env.VITE_API_URL || <span class="hljs-string">'https://telehealth-backend-2m1f.onrender.com/api/v1'</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [channel, setChannel] = useState(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> [clientReady, setClientReady] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">const</span> navigate = useNavigate();

  <span class="hljs-comment">// const ChatComponent = () =&gt; {</span>
    <span class="hljs-keyword">const</span> { user, token, Logout } = useStream();

    <span class="hljs-comment">// Always call the hook</span>
    <span class="hljs-keyword">const</span> chatClient = useCreateChatClient({
      apiKey,
      <span class="hljs-attr">tokenOrProvider</span>: token,
      <span class="hljs-attr">userData</span>: user?.id ? { <span class="hljs-attr">id</span>: user.id } : <span class="hljs-literal">undefined</span>,
    });

  <span class="hljs-comment">// Debug: See when user/token is ready</span>
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Stream user:"</span>, user);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Stream token:"</span>, token);
  }, [user, token]);

    <span class="hljs-comment">// Connect user to Stream</span>
    useEffect(<span class="hljs-function">() =&gt;</span> {
      <span class="hljs-keyword">const</span> connectUser = <span class="hljs-keyword">async</span> () =&gt; {
        <span class="hljs-keyword">if</span> (!chatClient || !user || !token || !user?.id) {
          <span class="hljs-built_in">console</span>.warn(<span class="hljs-string">"Missing chat setup data:"</span>, { chatClient, token, user });
          <span class="hljs-keyword">return</span>;
        }


        <span class="hljs-keyword">try</span> {
          <span class="hljs-keyword">await</span> chatClient.connectUser(
            {
              <span class="hljs-attr">id</span>: user.id,
              <span class="hljs-attr">name</span>: user.name || <span class="hljs-string">"Anonymous"</span>,
              <span class="hljs-attr">image</span>:
                user.image ||
                <span class="hljs-string">`https://getstream.io/random_png/?name=<span class="hljs-subst">${user.name || <span class="hljs-string">"user"</span>}</span>`</span>,
            },
            token
          );

          <span class="hljs-keyword">const</span> newChannel = chatClient.channel(<span class="hljs-string">"messaging"</span>, <span class="hljs-string">"my_general_chat"</span>, {
            <span class="hljs-attr">name</span>: <span class="hljs-string">"General Chat"</span>,
            <span class="hljs-attr">members</span>: [user.id],
          });

          <span class="hljs-keyword">await</span> newChannel.watch();
          setChannel(newChannel);
          setClientReady(<span class="hljs-literal">true</span>);
        } <span class="hljs-keyword">catch</span> (err) {
          <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error connecting user:"</span>, err);
        }
      };

      connectUser();
    }, [chatClient, user, token]);

    <span class="hljs-keyword">const</span> handleVideoCallClick = <span class="hljs-function">() =&gt;</span> {
      navigate(<span class="hljs-string">"/videoCall"</span>);
  };

  <span class="hljs-keyword">const</span> handleLogout = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">await</span> Logout();
    navigate(<span class="hljs-string">"/login"</span>);
  }

  <span class="hljs-keyword">if</span> (!user || !token) {
    <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-600"</span>&gt;</span>User or token not ready.<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
  }

  <span class="hljs-keyword">if</span> (!clientReady || !channel) <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Loading chat...<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;


  <span class="hljs-keyword">return</span> (
{ checkout the github repo}
            &lt;ChannelHeader /&gt;
            <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">MessageList</span> /&gt;</span></span>
            <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">MessageInput</span> /&gt;</span></span>
          &lt;/Window&gt;
          <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Thread</span> /&gt;</span></span>
        &lt;/Channel&gt;
      &lt;/Chat&gt;


      &lt;/div&gt;

    );
  }

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<h3 id="heading-video-setup"><strong>Video Setup</strong></h3>
<p>You’ll now set up the video function for your frontend. To do so, create a new file <code>VideoStream.jsx</code> and add the command:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React, { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { StreamVideoClient } <span class="hljs-keyword">from</span> <span class="hljs-string">"@stream-io/video-client"</span>;
<span class="hljs-keyword">import</span> { StreamVideo, StreamCall } <span class="hljs-keyword">from</span> <span class="hljs-string">"@stream-io/video-react-sdk"</span>;
<span class="hljs-keyword">import</span> { useNavigate } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;


<span class="hljs-keyword">import</span> { useStream } <span class="hljs-keyword">from</span> <span class="hljs-string">"./StreamContext"</span>;
<span class="hljs-keyword">import</span> { MyUILayout } <span class="hljs-keyword">from</span> <span class="hljs-string">"./MyUILayout"</span>;


<span class="hljs-keyword">const</span> apiKey = <span class="hljs-keyword">import</span>.meta.env.VITE_STREAM_API_KEY;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">VideoStream</span>(<span class="hljs-params"></span>) </span>{

  <span class="hljs-keyword">const</span> [client, setClient] = useState(<span class="hljs-literal">null</span>);
    <span class="hljs-keyword">const</span> [call, setCall] = useState(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> { user, token } = useStream();
  <span class="hljs-keyword">const</span> navigate = useNavigate();

  useEffect(<span class="hljs-function">() =&gt;</span> {

    <span class="hljs-keyword">let</span> clientInstance;
    <span class="hljs-keyword">let</span> callInstance;


    <span class="hljs-keyword">const</span> setup = <span class="hljs-keyword">async</span> () =&gt; {
      <span class="hljs-keyword">if</span> (!apiKey || !user || !token) <span class="hljs-keyword">return</span>;

      clientInstance = <span class="hljs-keyword">new</span> StreamVideoClient({ apiKey, user, token });

      callInstance = clientInstance.call(<span class="hljs-string">"default"</span>, user.id); <span class="hljs-comment">// Use user.id as callId</span>


      <span class="hljs-keyword">await</span> callInstance.join({ <span class="hljs-attr">create</span>: <span class="hljs-literal">true</span> });

      setClient(clientInstance);
      setCall(callInstance);
    };

    setup();

    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-keyword">if</span> (callInstance) callInstance.leave();
      <span class="hljs-keyword">if</span> (clientInstance) clientInstance.disconnectUser();

    };
  }, [user, token]);

  <span class="hljs-keyword">const</span> handleLeaveCall = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">if</span> (call) <span class="hljs-keyword">await</span> call.leave();
    <span class="hljs-keyword">if</span> (client) <span class="hljs-keyword">await</span> client.disconnectUser();

    setCall(<span class="hljs-literal">null</span>);
    setClient(<span class="hljs-literal">null</span>);

    navigate(<span class="hljs-string">"/dashboard"</span>); <span class="hljs-comment">// or any other route</span>
  };

  <span class="hljs-keyword">if</span> (!apiKey) <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Missing Stream API Key<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;

  <span class="hljs-keyword">if</span> (!client || !call)
    <span class="hljs-keyword">return</span> (
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center justify-center h-screen text-xl font-semibold"</span>&gt;</span>
    Connecting to the video call...
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"relative h-screen w-full p-2 sm:p-4 bg-gray-50"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">StreamVideo</span> <span class="hljs-attr">client</span>=<span class="hljs-string">{client}</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">StreamCall</span> <span class="hljs-attr">call</span>=<span class="hljs-string">{call}</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">MyUILayout</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">StreamCall</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">StreamVideo</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
        <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleLeaveCall}</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"absolute top-2 right-2 sm:top-4 sm:right-4 bg-red-600 text-white text-sm sm:text-base px-3 sm:px-4 py-1.5 sm:py-2 rounded-lg shadow hover:bg-red-700 transition"</span>
      &gt;</span>
        Leave Call
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
  }

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> VideoStream;
</code></pre>
<pre><code class="lang-javascript"><span class="hljs-comment">//MYUILayout.jsx</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> {
  useCall,
  useCallStateHooks,
  CallingState,
} <span class="hljs-keyword">from</span> <span class="hljs-string">'@stream-io/video-react-sdk'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyUILayout</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> call = useCall();
  <span class="hljs-keyword">const</span> { useCallCallingState, useParticipantCount } = useCallStateHooks();
  <span class="hljs-keyword">const</span> callingState = useCallCallingState();
  <span class="hljs-keyword">const</span> participantCount = useParticipantCount();

  <span class="hljs-keyword">if</span> (callingState !== CallingState.JOINED) {
    <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Joining call...<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
  }

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">padding:</span> '<span class="hljs-attr">1rem</span>', <span class="hljs-attr">fontSize:</span> '<span class="hljs-attr">1.2rem</span>' }}&gt;</span>
      ✅ Call "<span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>{call?.id}<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span>" has <span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>{participantCount}<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> participants.
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<h2 id="heading-project-demo">Project Demo</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752705861841/85b6d6b3-0f5e-402f-b8b5-8ab51d820403.gif" alt="Telehealth Final Project Demo" class="image--center mx-auto" width="1398" height="630" loading="lazy"></p>
<p>Congratulations! You have successfully integrated Stream’s chat and video function into your application.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>And that’s a wrap! </p>
<p>You’ve <a target="_blank" href="https://getstream.io/blog/telemedicine-app-development/">built a telehealth app</a> with secure video, real-time chat, and user authentication – all powered by Stream’s Chat and Video SDKs. </p>
<p>This foundation gives you the flexibility to expand further with features like appointment scheduling, patient history, or HIPPA-compliant file sharing. </p>
<p>You can find the <a target="_blank" href="https://github.com/Derekvibe/Telehealth_Backend">frontend</a> and <a target="_blank" href="https://github.com/Derekvibe/Telehealth_Frontend">backend</a> applications on GitHub. The frontend app is hosted using the Vercel hosting service, and the backend is hosted on Render.</p>
<p>Check out the <a target="_blank" href="https://telehealth-frontend.vercel.app/">repository of the application</a>.</p>
<p>Happy coding! 🚀</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn the MERN Stack in 2025 ]]>
                </title>
                <description>
                    <![CDATA[ If you’ve been meaning to learn full-stack web development but don’t know where to start, this new course is a solid way in. Whether you're aiming to get a job in web dev or just want to build your own projects, understanding how the pieces fit toget... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-the-mern-stack-in-2025/</link>
                <guid isPermaLink="false">68483a699e226a329abe4dee</guid>
                
                    <category>
                        <![CDATA[ MERN Stack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Tue, 10 Jun 2025 14:00:09 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1749234841421/0d452535-e019-4a1b-800a-1912cc16254a.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you’ve been meaning to learn full-stack web development but don’t know where to start, this new course is a solid way in. Whether you're aiming to get a job in web dev or just want to build your own projects, understanding how the pieces fit together is a huge step. And the MERN stack (MongoDB, Express, React, Node.js) is one of the most in-demand setups out there.</p>
<p>We just published a complete MERN stack course on the <a target="_blank" href="http://freeCodeCamp.org">freeCodeCamp.org</a> YouTube channel. It’s beginner-friendly, and it walks you through everything from setting up your development environment to deploying a real-world notes app with full CRUD functionality. Burak, your instructor, explains every step clearly and doesn’t skip over the parts that usually trip people up.</p>
<p>You’ll build something useful while picking up important backend concepts like route handling, controllers, middleware, and rate limiting. On the frontend, you’ll create a clean, responsive UI using React. It’s practical and hands-on, so by the end, you’ll not only understand how to build and connect a REST API with a frontend, but you’ll have deployed your app too.</p>
<h3 id="heading-whats-covered">What’s covered:</h3>
<ul>
<li><p><strong>Project Preview:</strong> A quick look at what you'll be building: a simple but polished notes app.</p>
</li>
<li><p><strong>Backend Setup:</strong> Start by initializing your Node.js project and learning the basics of Express.</p>
</li>
<li><p><strong>Nodemon &amp; Routes:</strong> Set up development tools like Nodemon and build your first API routes.</p>
</li>
<li><p><strong>Organizing Your Code:</strong> Structure your folders and files in a clean, scalable way.</p>
</li>
<li><p><strong>MongoDB:</strong> Connect to a MongoDB database and start storing your notes.</p>
</li>
<li><p><strong>Controllers:</strong> Create controller functions to handle your API logic cleanly.</p>
</li>
<li><p><strong>Middleware &amp; Rate Limiting:</strong> Add middleware functions for error handling and security features like rate limiting.</p>
</li>
<li><p><strong>Frontend Setup:</strong> Start building the React frontend and connect it to your backend.</p>
</li>
<li><p><strong>Home Page:</strong> Display all notes pulled from the backend.</p>
</li>
<li><p><strong>Create Page:</strong> Add the UI and logic for creating new notes.</p>
</li>
<li><p><strong>Delete Functionality:</strong> Let users delete notes and sync the frontend with the backend.</p>
</li>
<li><p><strong>Detail Page:</strong> Show individual note details on a separate page.</p>
</li>
<li><p><strong>Deployment:</strong> Learn how to deploy the whole app, step by step, with extra attention to details that most tutorials skip.</p>
</li>
</ul>
<p>If you’re serious about learning web dev or need a project for your portfolio, this is a great place to start. Watch the full course now on the <a target="_blank" href="https://youtu.be/F9gB5b4jgOI">freeCodeCamp.org YouTube channel</a>.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/F9gB5b4jgOI" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a MERN Stack To-Do App ]]>
                </title>
                <description>
                    <![CDATA[ This guide will walk you through building a full-stack MERN To-Do application. It covers setting up the environment, writing code to demonstrate core CRUD (Create, Read, Update, Delete) operations, and connecting the application to MongoDB Atlas, a f... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-mern-stack-to-do-app/</link>
                <guid isPermaLink="false">67c74d6473daa61c95803cfc</guid>
                
                    <category>
                        <![CDATA[ MERN Stack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MongoDB ]]>
                    </category>
                
                    <category>
                        <![CDATA[ todoapp ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Casmir Onyekani ]]>
                </dc:creator>
                <pubDate>Tue, 04 Mar 2025 18:58:44 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1741102112733/3aa43545-c095-4a47-8787-130b470f6ce1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>This guide will walk you through building a full-stack MERN To-Do application. It covers setting up the environment, writing code to demonstrate core CRUD (Create, Read, Update, Delete) operations, and connecting the application to MongoDB Atlas, a free cloud database.</p>
<p>Before diving into this article, I recommend that you have a foundational understanding of HTML, CSS, and JavaScript, as well as some knowledge of frontend and backend frameworks and libraries.</p>
<p>My primary focus will be on functionality, allowing you to customize the design as you see fit. The commands I’ll use here are tailored for Windows, so if you're using Linux, macOS, or Ubuntu, you may need to adjust them accordingly.</p>
<p>By the end of this guide, you'll have a fully functional To-Do app up and running on your system.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-introduction-to-the-mern-stack">Introduction to the MERN Stack</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-your-development-environment">How to Set Up Your Development environment</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-install-nodejs-and-npm-node-package-manager">Install Node.js and npm - Node Package Manager</a></li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-a-new-mern-project">How to Set Up a New MERN Project</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-frontend-setup">Frontend Setup</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-build-the-to-do-app-ui">Build the To-Do App UI</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-displaying-your-tasks-in-the-ui">Displaying your tasks in the UI</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-give-your-app-customized-styling">Give Your App Customized Styling</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-backend-setup">Backend Setup</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-set-up-mongodb-atlas">Set Up MongoDB Atlas</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-run-the-application">Run the Application</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-introduction-to-the-mern-stack">Introduction to the MERN Stack</h2>
<p>The MERN stack is a popular JavaScript stack for building modern web applications. It consists of:</p>
<ul>
<li><p><strong>MongoDB</strong>: A NoSQL database for storing data.</p>
</li>
<li><p><strong>Express.js</strong>: A backend framework for building APIs.</p>
</li>
<li><p><strong>React (UI library) + Vite (build tool) + TypeScript (typed JavaScript)</strong>: A modern frontend stack for building scalable and maintainable user interfaces.</p>
</li>
<li><p><strong>Node.js</strong>: A runtime environment for executing JavaScript on the server.</p>
</li>
</ul>
<h2 id="heading-how-to-set-up-your-development-environment">How to Set Up Your Development environment</h2>
<h3 id="heading-install-nodejs-and-npm-node-package-manager">Install Node.js and npm (Node Package Manager)</h3>
<p>Instead of installing Node.js and npm in your project folder, I advise you to install them in your system's root directory so that you can use them in any project, not just this one.</p>
<p>First, download and install Node.js (which includes npm) from the <a target="_blank" href="https://nodejs.org/en">official website</a> if you don’t have it already.</p>
<p>After installation, open your command line (I am using Git Bash) and verify the installation by running the following commands:</p>
<pre><code class="lang-bash">node -v
npm -v
</code></pre>
<p>You should see the installed versions of Node.js and npm if correctly installed.</p>
<h2 id="heading-how-to-set-up-a-new-mern-project">How to Set Up a New MERN Project</h2>
<p>Create a project folder and open your code editor by running these commands:</p>
<pre><code class="lang-bash">mkdir mern-todo-app
<span class="hljs-built_in">cd</span> mern-todo-app
code .
</code></pre>
<p>The command <code>code .</code> automatically opens VS Code. If it doesn’t, open VS Code manually and navigate to your <code>mern-todo-app</code> folder.</p>
<h3 id="heading-frontend-setup">Frontend Setup</h3>
<h4 id="heading-set-up-vite-with-react-and-typescript">Set Up Vite with React and TypeScript</h4>
<p>Make sure you are in your project root directory (<code>mern-todo-app</code>), then run the following command:</p>
<pre><code class="lang-bash">npm create vite@latest frontend --template react-ts
</code></pre>
<p>This command will create a TypeScript-based React frontend inside the <code>frontend</code> folder within your <code>mern-todo-app</code> directory.</p>
<h4 id="heading-install-axios-for-making-api-requests">Install Axios for Making API Requests</h4>
<p>Axios is a popular JavaScript library used to make HTTP requests from the frontend to a backend API. It simplifies sending GET, POST, PUT, and DELETE requests and handling responses.</p>
<p>To install Axios, run the following command:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> frontend
npm install axios
</code></pre>
<h3 id="heading-build-the-to-do-app-ui">Build the To-Do App UI</h3>
<p>Inside the <code>src</code> folder, create an <code>App.tsx</code> file if it doesn’t already exist, and add the below code. It’s a lot, but don’t worry – I’ll break it down bit by bit afterwards:</p>
<p><code>frontend/src/App.tsx</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// BLOCK 1: Importing Dependencies</span>
<span class="hljs-keyword">import</span> React, { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;
<span class="hljs-keyword">import</span> TodoList <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/TodoList.tsx"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;

<span class="hljs-comment">// BLOCK 2: Defining Task Interface</span>
interface Task {
  <span class="hljs-attr">_id</span>: string;
  title: string;
  completed: boolean;
}

<span class="hljs-comment">// BLOCK 3: Setting Up State Variables</span>
<span class="hljs-keyword">const</span> App: React.FC = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-comment">// State for tasks, new task text, and editing controls</span>
  <span class="hljs-keyword">const</span> [tasks, setTasks] = useState&lt;Task[]&gt;([]);
  <span class="hljs-keyword">const</span> [task, setTask] = useState&lt;string&gt;(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [editingTaskId, setEditingTaskId] = useState&lt;string | <span class="hljs-literal">null</span>&gt;(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> [editingTitle, setEditingTitle] = useState&lt;string&gt;(<span class="hljs-string">""</span>);

  <span class="hljs-comment">// BLOCK 4: Fetch tasks from the backend on component mount</span>
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> fetchTasks = <span class="hljs-keyword">async</span> () =&gt; {
      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axios.get&lt;Task[]&gt;(<span class="hljs-string">`http://localhost:5000/api/tasks`</span>);
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Fetched tasks:"</span>, response.data); <span class="hljs-comment">// Debugging log</span>
        setTasks(response.data);
      } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error fetching tasks:"</span>, error);
      }
    };
    fetchTasks();
  }, []);

  <span class="hljs-comment">// BLOCK 5: Adding a Task</span>
  <span class="hljs-keyword">const</span> addTask = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">if</span> (!task) <span class="hljs-keyword">return</span>;

    <span class="hljs-keyword">try</span> {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Adding task:"</span>, task); <span class="hljs-comment">// Debugging log</span>
      <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axios.post&lt;Task&gt;(
        <span class="hljs-string">`http://localhost:5000/api/tasks`</span>,
        { <span class="hljs-attr">title</span>: task },
        { <span class="hljs-attr">headers</span>: { <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span> } }
      );
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Task added response:"</span>, response.data);
      setTasks([...tasks, response.data]);
      setTask(<span class="hljs-string">""</span>);
    } <span class="hljs-keyword">catch</span> (error) {
      <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error adding task:"</span>, error);
    }
  };

  <span class="hljs-comment">// BLOCK 6: Delete a task</span>
  <span class="hljs-keyword">const</span> deleteTask = <span class="hljs-keyword">async</span> (id: string) =&gt; {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> axios.delete(<span class="hljs-string">`http://localhost:5000/api/tasks/<span class="hljs-subst">${id}</span>`</span>);
      setTasks(tasks.filter(<span class="hljs-function">(<span class="hljs-params">t</span>) =&gt;</span> t._id !== id));
    } <span class="hljs-keyword">catch</span> (error) {
      <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error deleting task:"</span>, error);
    }
  };

  <span class="hljs-comment">// BLOCK 7: Updating a Task</span>
  <span class="hljs-keyword">const</span> updateTask = <span class="hljs-keyword">async</span> (id: string, <span class="hljs-attr">updatedTask</span>: Partial&lt;Task&gt;) =&gt; {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axios.put(
        <span class="hljs-string">`http://localhost:5000/api/tasks/<span class="hljs-subst">${id}</span>`</span>,
        updatedTask,
        { <span class="hljs-attr">headers</span>: { <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span> } }
      );

      setTasks(
        tasks.map(<span class="hljs-function">(<span class="hljs-params">task</span>) =&gt;</span>
          task._id === id ? { ...task, ...response.data } : task
        )
      );
      setEditingTaskId(<span class="hljs-literal">null</span>);
      setEditingTitle(<span class="hljs-string">""</span>);
    } <span class="hljs-keyword">catch</span> (error) {
      <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error updating task:"</span>, error);
    }
  };

  <span class="hljs-comment">// BLOCK 8: Handling Edits</span>
  <span class="hljs-keyword">const</span> startEditing = <span class="hljs-function">(<span class="hljs-params">id: string</span>) =&gt;</span> {
    setEditingTaskId(id);
  };

  <span class="hljs-comment">// Handle title change during editing</span>
  <span class="hljs-keyword">const</span> handleEditChange = <span class="hljs-function">(<span class="hljs-params">e: React.ChangeEvent&lt;HTMLInputElement&gt;</span>) =&gt;</span> {
    setEditingTitle(e.target.value);
  };

  <span class="hljs-comment">// BLOCK 9: Render the app</span>
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Todo App<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
          <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
          <span class="hljs-attr">value</span>=<span class="hljs-string">{task}</span>
          <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setTask(e.target.value)}
        /&gt;
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{addTask}</span>&gt;</span>Add Task<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">TodoList</span>
        <span class="hljs-attr">tasks</span>=<span class="hljs-string">{tasks}</span>
        <span class="hljs-attr">deleteTask</span>=<span class="hljs-string">{deleteTask}</span>
        <span class="hljs-attr">updateTask</span>=<span class="hljs-string">{updateTask}</span>
        <span class="hljs-attr">editingTitle</span>=<span class="hljs-string">{editingTitle}</span>
        <span class="hljs-attr">setEditingTitle</span>=<span class="hljs-string">{setEditingTitle}</span>
        <span class="hljs-attr">editingTaskId</span>=<span class="hljs-string">{editingTaskId}</span>
        <span class="hljs-attr">setEditingTaskId</span>=<span class="hljs-string">{setEditingTaskId}</span>
        <span class="hljs-attr">startEditing</span>=<span class="hljs-string">{startEditing}</span>
        <span class="hljs-attr">handleEditChange</span>=<span class="hljs-string">{handleEditChange}</span>
      /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};

<span class="hljs-comment">// BLOCK 10: Exporting the Component</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>Here’s a block-by-block breakdown of the code above:</p>
<p><strong>BLOCK 1:</strong> Importing dependencies</p>
<ul>
<li><p><code>React, { useState, useEffect }</code>: Manages component state and side effects.</p>
</li>
<li><p><code>axios</code>: Handles API requests.</p>
</li>
<li><p><code>TodoList.tsx</code>: A child component to display and manage tasks.</p>
</li>
<li><p><code>App.css</code>: Styles the app.</p>
</li>
</ul>
<p><strong>BLOCK 2:</strong> Defining the task interface</p>
<ul>
<li>Defines the structure of a task (<code>_id</code>, <code>title</code>, <code>completed</code>).</li>
</ul>
<p><strong>BLOCK 3:</strong> Setting up state variables</p>
<ul>
<li><p><code>tasks</code>: Stores the list of tasks.</p>
</li>
<li><p><code>task</code>: Holds input for new tasks.</p>
</li>
<li><p><code>editingTaskId</code>: Tracks the task being edited.</p>
</li>
<li><p><code>editingTitle</code>: Stores the updated title while editing.</p>
</li>
</ul>
<p><strong>BLOCK 4:</strong> Fetching tasks from the backend (<code>useEffect</code>)</p>
<ul>
<li><p>Runs once when the app loads.</p>
</li>
<li><p>Calls the API (<code>GET /api/tasks</code>) to get tasks and updates <code>tasks</code>.</p>
</li>
<li><p>Error handling**:** Logs an error message if the fetching request fails</p>
</li>
</ul>
<p><strong>BLOCK 5:</strong> Adding a task</p>
<ul>
<li><p>Sends a <code>POST</code> request to add a new task.</p>
</li>
<li><p>Updates <code>tasks</code> with the new task.</p>
</li>
<li><p>Error handling**:** Logs an error message if the adding task request fails</p>
</li>
</ul>
<p><strong>BLOCK 6:</strong> Deleting a task</p>
<ul>
<li><p>Sends a <code>DELETE</code> request to remove a task.</p>
</li>
<li><p>Updates <code>tasks</code> by filtering out the deleted task.</p>
</li>
<li><p>Error handling**:** Logs an error message if the deleting task request fails</p>
</li>
</ul>
<p><strong>BLOCK 7:</strong> Updating a task</p>
<ul>
<li><p>Sends a <code>PUT</code> request to update a task’s title.</p>
</li>
<li><p>Updates <code>tasks</code> with the new title.</p>
</li>
<li><p>Error handling**:** Logs an error message if the update request fails</p>
</li>
</ul>
<p><strong>BLOCK 8:</strong> Handling edits</p>
<ul>
<li><p><code>startEditing(id)</code>: Sets a task into edit mode.</p>
</li>
<li><p><code>handleEditChange(e)</code>: Updates the editing input.</p>
</li>
</ul>
<p><strong>BLOCK 9:</strong> Rendering the UI</p>
<ul>
<li><p>Displays an input field and button to add tasks.</p>
</li>
<li><p>Passes task data and functions (<code>deleteTask</code>, <code>updateTask</code>, etc.) to <code>TodoList.tsx</code>.</p>
</li>
</ul>
<p><strong>BLOCK 10:</strong> Exporting the component</p>
<ul>
<li><code>export default App;</code>: Makes <code>App</code> usable in other files.</li>
</ul>
<h3 id="heading-displaying-your-tasks-in-the-ui">Displaying your tasks in the UI</h3>
<p>Inside the <code>src</code> folder, create a new folder named <code>components</code>. Then add a <code>TodoList.tsx</code> file inside it with the below code.</p>
<p><code>src/components/TodoList.tsx</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// BLOCK 1: Importing Dependencies</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-comment">// BLOCK 2: Defining Interfaces</span>
interface Task {
  <span class="hljs-attr">_id</span>: string; <span class="hljs-comment">// Unique ID for the task</span>
  title: string; <span class="hljs-comment">// Task name</span>
  completed: boolean; <span class="hljs-comment">// True if done, False if not</span>
}

interface TodoListProps {
  <span class="hljs-attr">tasks</span>: Task[];
  deleteTask: <span class="hljs-function">(<span class="hljs-params">id: string</span>) =&gt;</span> <span class="hljs-keyword">void</span>;
  updateTask: <span class="hljs-function">(<span class="hljs-params">id: string, updatedTask: Partial&lt;Task&gt;</span>) =&gt;</span> <span class="hljs-keyword">void</span>;
  editingTitle: string;
  setEditingTitle: <span class="hljs-function">(<span class="hljs-params">title: string</span>) =&gt;</span> <span class="hljs-keyword">void</span>;
  editingTaskId: string | <span class="hljs-literal">null</span>;
  setEditingTaskId: <span class="hljs-function">(<span class="hljs-params">id: string | <span class="hljs-literal">null</span></span>) =&gt;</span> <span class="hljs-keyword">void</span>;
  startEditing: <span class="hljs-function">(<span class="hljs-params">id: string</span>) =&gt;</span> <span class="hljs-keyword">void</span>;
  handleEditChange: <span class="hljs-function">(<span class="hljs-params">e: React.ChangeEvent&lt;HTMLInputElement&gt;</span>) =&gt;</span> <span class="hljs-keyword">void</span>;
}

<span class="hljs-comment">// BLOCK 3: Declares the TodoList Component</span>
<span class="hljs-keyword">const</span> TodoList: React.FC&lt;TodoListProps&gt; = <span class="hljs-function">(<span class="hljs-params">{
  tasks,
  deleteTask,
  updateTask,
  editingTitle,
  setEditingTitle,
  editingTaskId,
  setEditingTaskId,
  startEditing,
  handleEditChange,
}</span>) =&gt;</span> {

  <span class="hljs-comment">// BLOCK 4: Rendering the Task List and handling task actions</span>
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
      {tasks.map((task) =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{task._id}</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span>
            <span class="hljs-attr">checked</span>=<span class="hljs-string">{task.completed}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{()</span> =&gt;</span> updateTask(task._id, { completed: !task.completed })}
          /&gt;
          {editingTaskId === task._id ? (
            <span class="hljs-tag">&lt;&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{editingTitle}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleEditChange}</span> /&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> {
                  updateTask(task._id, { title: editingTitle });
                  setEditingTaskId(null);
                }}
              &gt;
                Save
              <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/&gt;</span>
          ) : (
            <span class="hljs-tag">&lt;&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">textDecoration:</span> <span class="hljs-attr">task.completed</span> ? "<span class="hljs-attr">line-through</span>" <span class="hljs-attr">:</span> "<span class="hljs-attr">none</span>" }}&gt;</span>
                {task.title}
              <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>

              <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> deleteTask(task._id)}&gt;Delete<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> {
                    startEditing(task._id);
                    setEditingTitle(task.title);
                  }}
                &gt;
                  Edit
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/&gt;</span></span>
          )}
        &lt;/li&gt;
      ))}
    &lt;/ul&gt;
  );
};

<span class="hljs-comment">// BLOCK 5: Exporting the Component</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> TodoList;
</code></pre>
<p>Here’s a block-by-block breakdown of the code above:</p>
<p><strong>BLOCK 1:</strong> Importing dependencies</p>
<ul>
<li>React: Enables functional component creation.</li>
</ul>
<p><strong>BLOCK 2:</strong> Defining interfaces</p>
<ul>
<li><p>Task interface: Defines <code>_id</code>, <code>title</code>, and <code>completed</code> properties.</p>
</li>
<li><p>TodoListProps interface: Defines props passed to the <code>TodoList</code> component</p>
</li>
</ul>
<p><strong>BLOCK 3</strong>: Declares the <code>TodoList</code> component</p>
<ul>
<li><p>Defines a functional React component (<code>TodoList</code>) using TypeScript (<code>React.FC&lt;TodoListProps&gt;</code>).</p>
</li>
<li><p>Extracts the listed props from <code>TodoListProps</code> and prepares the component for rendering.</p>
</li>
</ul>
<p><strong>BLOCK 4</strong>: Rendering the Task List and handling task actions</p>
<ul>
<li><p>Maps through <code>tasks</code> to display each task inside a <code>&lt;ul&gt;</code>.</p>
</li>
<li><p>Checkbox toggles <code>completed</code> status using <code>updateTask()</code>.</p>
</li>
<li><p>Conditional rendering:</p>
<ul>
<li><p>If a task is being edited, an input field appears for editing.</p>
</li>
<li><p>Otherwise, the task title is displayed with a strikethrough if completed</p>
</li>
</ul>
</li>
<li><p>Save button: Updates the task title using <code>updateTask()</code>, then exits edit mode.</p>
</li>
<li><p>Delete button: Calls <code>deleteTask()</code> to remove a task.</p>
</li>
<li><p>Edit button: Enables edit mode, setting <code>editingTaskId</code> and <code>editingTitle</code>.</p>
</li>
</ul>
<p><strong>BLOCK 5</strong>: Exporting the component</p>
<ul>
<li>Makes <code>TodoList</code> available for use in other components.</li>
</ul>
<h3 id="heading-give-your-app-customized-styling">Give Your App Customized Styling</h3>
<p>Inside your <code>src</code> folder, create <code>App.css</code> if it doesn’t exist and replace the content with your desired styling. Let’s give the frontend a finishing touch.</p>
<p><code>src/App.css</code>:</p>
<pre><code class="lang-css"><span class="hljs-comment">/* Center the app in the middle of the screen */</span>
<span class="hljs-selector-tag">html</span>, <span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">justify-content</span>: center;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">100vh</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">font-family</span>: Arial, sans-serif;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#f4f4f4</span>; <span class="hljs-comment">/* Light gray background */</span>
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">overflow-x</span>: hidden; <span class="hljs-comment">/* Prevent horizontal scrolling */</span>
}

<span class="hljs-comment">/* Style the main app container */</span>
<span class="hljs-selector-class">.App</span> {
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">background</span>: white;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">10px</span>; <span class="hljs-comment">/* Rounded corners */</span>
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">10px</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.1</span>); <span class="hljs-comment">/* Light shadow effect */</span>
  <span class="hljs-attribute">width</span>: <span class="hljs-number">90%</span>; <span class="hljs-comment">/* Make it flexible */</span>
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">350px</span>; <span class="hljs-comment">/* Prevent exceeding max size */</span>
  <span class="hljs-attribute">box-sizing</span>: border-box;
}

<span class="hljs-comment">/* Add spacing below the title */</span>
<span class="hljs-selector-tag">h1</span> {
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">20px</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">24px</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#333</span>;
}

<span class="hljs-comment">/* Style input fields */</span>
<span class="hljs-selector-tag">input</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>; <span class="hljs-comment">/* Full width */</span>
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">8px</span>;
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">10px</span>;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ccc</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
  <span class="hljs-attribute">box-sizing</span>: border-box;
}

<span class="hljs-comment">/* Style buttons */</span>
<span class="hljs-selector-tag">button</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>; <span class="hljs-comment">/* Make buttons full width */</span>
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>;
  <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">5px</span>;
  <span class="hljs-attribute">border</span>: none;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#007bff</span>; <span class="hljs-comment">/* Blue background */</span>
  <span class="hljs-attribute">color</span>: white;
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">14px</span>;
  <span class="hljs-attribute">transition</span>: background-color <span class="hljs-number">0.3s</span> ease-in-out;
}

<span class="hljs-comment">/* Change button color when hovered */</span>
<span class="hljs-selector-tag">button</span><span class="hljs-selector-pseudo">:hover</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#0056b3</span>;
}

<span class="hljs-comment">/* Remove default list styles */</span>
<span class="hljs-selector-tag">ul</span> {
  <span class="hljs-attribute">list-style</span>: none;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
}

<span class="hljs-comment">/* Style list items */</span>
<span class="hljs-selector-tag">li</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">justify-content</span>: space-between;
  <span class="hljs-attribute">background</span>: <span class="hljs-number">#fff</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">5px</span> <span class="hljs-number">0</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">2px</span> <span class="hljs-number">5px</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.1</span>); <span class="hljs-comment">/* Add a subtle shadow */</span>
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">box-sizing</span>: border-box;
}

<span class="hljs-comment">/* Allow task text to take available space */</span>
<span class="hljs-selector-tag">span</span> {
  <span class="hljs-attribute">flex</span>: <span class="hljs-number">1</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#333</span>;
}

<span class="hljs-comment">/* Style completed tasks */</span>
<span class="hljs-selector-tag">span</span><span class="hljs-selector-class">.completed</span> {
  <span class="hljs-attribute">text-decoration</span>: line-through;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#888</span>;
}

<span class="hljs-comment">/* Adjust the width of input fields inside the list */</span>
<span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"text"</span>]</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">70%</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">8px</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ccc</span>;
}

<span class="hljs-comment">/* Style the checkbox */</span>
<span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"checkbox"</span>]</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">18px</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">18px</span>;
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">accent-color</span>: <span class="hljs-number">#007bff</span>; <span class="hljs-comment">/* Blue checkbox to match buttons */</span>
  <span class="hljs-attribute">margin-right</span>: <span class="hljs-number">10px</span>;
}

<span class="hljs-comment">/* Styling for editing mode */</span>
<span class="hljs-selector-class">.editing-container</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">gap</span>: <span class="hljs-number">10px</span>;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
}

<span class="hljs-comment">/* Responsive styling for smaller screens */</span>
<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">400px</span>) {
  <span class="hljs-selector-class">.App</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">95%</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">15px</span>;
    <span class="hljs-attribute">max-width</span>: none; <span class="hljs-comment">/* Remove fixed width restriction */</span>
  }

  <span class="hljs-selector-tag">li</span> {
    <span class="hljs-attribute">flex-direction</span>: column;
    <span class="hljs-attribute">align-items</span>: flex-start;
  }

  <span class="hljs-selector-tag">input</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  }

  <span class="hljs-selector-tag">button</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>;
  }
}
</code></pre>
<p>Here’s what this CSS code does:</p>
<p>First, it centers the app (<code>html, body</code>):</p>
<ul>
<li><p>Uses <code>flexbox</code> to center the app vertically and horizontally.</p>
</li>
<li><p>Sets <code>height: 100vh</code> for full-screen height.</p>
</li>
<li><p>Prevents horizontal scrolling with <code>overflow-x: hidden</code>.</p>
</li>
</ul>
<p>Then it styles the main app container (<code>.App</code>):</p>
<ul>
<li><p>Adds a white background with rounded corners and a shadow.</p>
</li>
<li><p>Ensures responsiveness with <code>width: 90%</code> and <code>max-width: 350px</code>.</p>
</li>
</ul>
<p>Next, we handle typography and layout:</p>
<ul>
<li><p>Sets <code>Arial, sans-serif</code> as the font.</p>
</li>
<li><p>Adds spacing below the title (<code>h1</code>).</p>
</li>
<li><p>Ensures task text takes available space with <code>span { flex: 1; }</code>.</p>
</li>
</ul>
<p>Then we deal with input and button styling:</p>
<ul>
<li><p>Inputs are full-width, styled with padding, borders, and rounded corners.</p>
</li>
<li><p>Buttons are blue, full-width, with hover effects (<code>background-color: #0056b3</code>).</p>
</li>
</ul>
<p>And then task list styling <strong>(</strong><code>ul, li, span.completed</code>):</p>
<ul>
<li><p>Removes default list styles.</p>
</li>
<li><p>Each task (<code>li</code>) has a white background, padding, rounded corners, and a shadow.</p>
</li>
<li><p>Completed tasks are styled with a <code>line-through</code> and faded text color.</p>
</li>
</ul>
<p>Next, we handle checkbox and editing mode styling:</p>
<ul>
<li><p>Styled blue checkboxes (<code>accent-color: #007bff</code>).</p>
</li>
<li><p>Adds an <code>editing-container</code> with <code>display: flex;</code> for edit mode.</p>
</li>
</ul>
<p>And finally, we make the design responsive (<code>@media (max-width: 400px)</code>):</p>
<ul>
<li><p>Adjusts <code>.App</code> width and padding for small screens.</p>
</li>
<li><p>Stacks list items (<code>li</code>) vertically instead of side-by-side.</p>
</li>
</ul>
<h3 id="heading-backend-setup">Backend Setup</h3>
<p>In your VS Code terminal, make sure you are in your project root directory (inside <code>mern-todo-app</code>) and then create a folder called <code>backend</code>. Navigate to the <code>backend</code> folder and initialize <code>Node.js</code>:</p>
<pre><code class="lang-bash">mkdir backend
<span class="hljs-built_in">cd</span> backend
npm init -y
</code></pre>
<h4 id="heading-install-dependencies">Install Dependencies</h4>
<p>Still inside your <code>backend</code> folder, run this command:</p>
<pre><code class="lang-bash">npm install express mongoose dotenv cors
</code></pre>
<p>In this command,</p>
<ul>
<li><p><code>express</code> is a fast and minimal web framework for Node.js used to create server-side applications and APIs.</p>
</li>
<li><p><code>mongoose</code> is an Object Data Modeling (ODM) library for MongoDB, simplifying database interactions.</p>
</li>
<li><p><code>dotenv</code> loads environment variables from a <code>.env</code> file, keeping sensitive data secure.</p>
</li>
<li><p><code>cors</code> enables Cross-Origin Resource Sharing, allowing frontend applications to communicate with the backend across different domains.</p>
</li>
</ul>
<h4 id="heading-create-a-serverjs-file">Create a server.js File</h4>
<p>Inside your <code>backend</code> folder, create a file named <code>server.js</code> and enter the following code:</p>
<p><code>backend/server.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// BLOCK 1: Importing Dependencies</span>
<span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>);
<span class="hljs-keyword">const</span> mongoose = <span class="hljs-built_in">require</span>(<span class="hljs-string">"mongoose"</span>);
<span class="hljs-keyword">const</span> cors = <span class="hljs-built_in">require</span>(<span class="hljs-string">"cors"</span>);
<span class="hljs-keyword">const</span> dotenv = <span class="hljs-built_in">require</span>(<span class="hljs-string">"dotenv"</span>);

<span class="hljs-comment">// BLOCK 2: Configuring the Express App</span>
dotenv.config();

<span class="hljs-keyword">const</span> app = express();

<span class="hljs-comment">// BLOCK 3: Setting Up Middleware</span>
app.use(cors());
app.use(express.json());

<span class="hljs-comment">// BLOCK 4: Connecting to MongoDB</span>
<span class="hljs-keyword">const</span> connectDB = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">await</span> mongoose.connect(process.env.MONGO_URI);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"MongoDB Connected"</span>);
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"MongoDB Connection Failed:"</span>, err);
    process.exit(<span class="hljs-number">1</span>); <span class="hljs-comment">// Exit process with failure</span>
  }
};

<span class="hljs-comment">// Call the database connection function</span>
connectDB();

<span class="hljs-comment">// BLOCK 5: Defining Routes</span>
<span class="hljs-keyword">const</span> tasksRoutes = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./routes/tasks"</span>);
app.use(<span class="hljs-string">"/api/tasks"</span>, tasksRoutes);

<span class="hljs-comment">// BLOCK 6: Starting the Server</span>
<span class="hljs-keyword">const</span> PORT = process.env.PORT || <span class="hljs-number">5000</span>;
app.listen(PORT, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server running on port <span class="hljs-subst">${PORT}</span>`</span>);
});
</code></pre>
<p>Here’s a block-by-block breakdown of the code above:</p>
<p><strong>BLOCK 1</strong>: Importing dependencies</p>
<ul>
<li><p>express**:** Creates the server.</p>
</li>
<li><p>mongoose**:** Connects to MongoDB.</p>
</li>
<li><p>cors**:** Enables cross-origin requests.</p>
</li>
<li><p>dotenv**:** Loads environment variables.</p>
</li>
</ul>
<p><strong>BLOCK 2</strong>: Configuring the Express app</p>
<ul>
<li><p>Loads environment variables using <code>dotenv.config()</code>.</p>
</li>
<li><p>Initializes <code>express()</code> to create an app instance.</p>
</li>
</ul>
<p><strong>BLOCK 3</strong>: Setting up middleware</p>
<ul>
<li><p>cors()<strong>:</strong> Allows API access from different origins.</p>
</li>
<li><p>express.json()<strong>:</strong> Parses incoming JSON requests.</p>
</li>
</ul>
<p><strong>BLOCK 4</strong>: Connecting to MongoDB</p>
<ul>
<li><p>Defines <code>connectDB()</code> to connect to MongoDB using <code>MONGO_URI</code>.</p>
</li>
<li><p>Logs success or failure and exits on error.</p>
</li>
</ul>
<p><strong>BLOCK 5</strong>: Defining routes</p>
<ul>
<li><p>Imports <code>tasksRoutes</code> from <code>./routes/tasks</code>.</p>
</li>
<li><p>Uses <code>/api/tasks</code> as the base route for task operations.</p>
</li>
</ul>
<p><strong>BLOCK 6</strong>: Starting the server</p>
<ul>
<li><p>Sets <code>PORT</code> from <code>.env</code> or defaults to <code>5000</code>.</p>
</li>
<li><p>Starts the server and logs the running port.</p>
</li>
</ul>
<h4 id="heading-define-task-model">Define Task Model</h4>
<p>In your <code>backend</code> folder, create a <code>model</code> folder. Inside <code>model</code>, create a file named <code>Task.js</code> and add the following code:</p>
<p><code>backend/model/Task.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// BLOCK 1: Importing Mongoose</span>
<span class="hljs-keyword">const</span> mongoose = <span class="hljs-built_in">require</span>(<span class="hljs-string">"mongoose"</span>);

<span class="hljs-comment">// BLOCK 2: Defining Task Schema</span>
<span class="hljs-keyword">const</span> TaskSchema = <span class="hljs-keyword">new</span> mongoose.Schema({
    <span class="hljs-attr">title</span>: { <span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>, <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span> },
    <span class="hljs-attr">completed</span>: { <span class="hljs-attr">type</span>: <span class="hljs-built_in">Boolean</span>, <span class="hljs-attr">default</span>: <span class="hljs-literal">false</span> },
});

<span class="hljs-comment">// BLOCK 3: Creating and Exporting the Model</span>
<span class="hljs-built_in">module</span>.exports = mongoose.model(<span class="hljs-string">"Task"</span>, TaskSchema);
</code></pre>
<p>Here’s a block-by-block breakdown of the code above:</p>
<p><strong>BLOCK 1</strong>: Importing Mongoose</p>
<ul>
<li><code>mongoose</code>: Used to define the schema and interact with MongoDB.</li>
</ul>
<p><strong>BLOCK 2</strong>: Defining the task schema</p>
<ul>
<li><p><code>title</code>: A required string field for the task title.</p>
</li>
<li><p><code>completed</code>: A boolean field indicating task status (default: <code>false</code>).</p>
</li>
</ul>
<p><strong>BLOCK 3</strong>: Creating and exporting the model</p>
<ul>
<li><p>Creates a Mongoose model named <code>"Task"</code> based on <code>TaskSchema</code>.</p>
</li>
<li><p>Exports the model for use in other parts of the application</p>
</li>
</ul>
<h4 id="heading-define-routes">Define Routes</h4>
<p>In your <code>backend</code> folder, create a <code>routes</code> folder. Inside <code>routes</code>, create a file named <code>tasks.js</code> and add the following code:</p>
<p><code>backend/routes/tasks.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// BOCK 1: Import dependencies</span>
<span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>);
<span class="hljs-keyword">const</span> Task = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../models/Task"</span>);

<span class="hljs-keyword">const</span> router = express.Router();

<span class="hljs-comment">// BLOCK 2: GET all tasks</span>
router.get(<span class="hljs-string">"/"</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> tasks = <span class="hljs-keyword">await</span> Task.find();
    res.json(tasks);
  } <span class="hljs-keyword">catch</span> (err) {
    res.status(<span class="hljs-number">500</span>).json({ <span class="hljs-attr">error</span>: err.message });
  }
});

<span class="hljs-comment">// BLOCK 3: POST a new task</span>
router.post(<span class="hljs-string">"/"</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { title } = req.body;
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Received title:"</span>, title); <span class="hljs-comment">// Debugging log</span>

  <span class="hljs-keyword">if</span> (!title) {
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).json({ <span class="hljs-attr">error</span>: <span class="hljs-string">"Task title is required"</span> });
  }

  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> newTask = <span class="hljs-keyword">new</span> Task({ title });
    <span class="hljs-keyword">await</span> newTask.save();
    res.status(<span class="hljs-number">201</span>).json(newTask);
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-built_in">console</span>.error(err.message);
    res.status(<span class="hljs-number">500</span>).json({ <span class="hljs-attr">error</span>: err.message });
  }
});

<span class="hljs-comment">// BLOCK 4: DELETE a task</span>
router.delete(<span class="hljs-string">"/:id"</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> { id } = req.params;
    <span class="hljs-keyword">await</span> Task.findByIdAndDelete(id);
    res.json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Task deleted"</span> });
  } <span class="hljs-keyword">catch</span> (err) {
    res.status(<span class="hljs-number">500</span>).json({ <span class="hljs-attr">error</span>: err.message });
  }
});

<span class="hljs-comment">// BLOCK 5: UPDATE a task</span>
router.put(<span class="hljs-string">"/:id"</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> { id } = req.params;

    <span class="hljs-keyword">const</span> updatedTask = <span class="hljs-keyword">await</span> Task.findByIdAndUpdate(
      id,
      req.body,
      { <span class="hljs-attr">new</span>: <span class="hljs-literal">true</span> } <span class="hljs-comment">// Return the updated task</span>
    );
    res.json(updatedTask);
  } <span class="hljs-keyword">catch</span> (error) {
    res.status(<span class="hljs-number">500</span>).json({ <span class="hljs-attr">error</span>: <span class="hljs-string">"Error updating task"</span> });
  }
});

<span class="hljs-comment">// BLOCK 6: Export the router</span>
<span class="hljs-built_in">module</span>.exports = router;
</code></pre>
<p>Here’s a block-by-block breakdown of the code above:</p>
<p><strong>BLOCK 1</strong>: Import dependencies</p>
<ul>
<li><p>express: Handles routing.</p>
</li>
<li><p>Task: Imports the Task model.</p>
</li>
<li><p>express.Router(): Creates a router for task-related routes.</p>
</li>
</ul>
<p><strong>BLOCK 2</strong>: GET all tasks</p>
<ul>
<li><p>Fetches all tasks from the database.</p>
</li>
<li><p>Sends the tasks as a JSON response.</p>
</li>
<li><p>Handles errors with a 500 status.</p>
</li>
</ul>
<p><strong>BLOCK 3</strong>: POST a new task</p>
<ul>
<li><p>Extracts <code>title</code> from the request body.</p>
</li>
<li><p>Logs the received title for debugging.</p>
</li>
<li><p>Validates the title (returns 400 if missing).</p>
</li>
<li><p>Saves the task to the database and returns it.</p>
</li>
</ul>
<p><strong>BLOCK 4</strong>: DELETE a task</p>
<ul>
<li><p>Extracts <code>id</code> from the request params.</p>
</li>
<li><p>Deletes the task from the database.</p>
</li>
<li><p>Returns a success message.</p>
</li>
</ul>
<p><strong>BLOCK 5</strong>: UPDATE a task</p>
<ul>
<li><p>Extracts <code>id</code> from the request params.</p>
</li>
<li><p>Updates the task using request body data.</p>
</li>
<li><p>Returns the updated task.</p>
</li>
</ul>
<p><strong>BLOCK 6</strong>: Export the router</p>
<ul>
<li>Exports <code>router</code> for use in other parts of the app.</li>
</ul>
<p>This Express.js router handles CRUD operations for a <code>Task</code> model using MongoDB. It defines routes to get all tasks, add a new task, delete a task by ID, and update a task's title by ID. Error handling ensures proper responses for missing data or server issues.</p>
<h4 id="heading-create-a-env-file">Create a .env file</h4>
<p>In your backend folder, create a <code>.env</code> file and add the following:</p>
<p><code>backend/.env</code>:</p>
<pre><code class="lang-javascript">MONGO_URI=your_mongodb_atlas_uri
</code></pre>
<h3 id="heading-set-up-mongodb-atlas">Set Up MongoDB Atlas</h3>
<p>MongoDB Atlas is a cloud-based MongoDB service. We'll use the free tier for this project.</p>
<p>To get started, go to <a target="_blank" href="https://www.mongodb.com/products/platform/atlas-database">MongoDB Atlas</a> and create an account or log in.</p>
<p>Follow the steps to create a free cluster. Once the cluster is created, click Connect and follow the instructions to:</p>
<ul>
<li><p>Whitelist your IP address to allow MongoDB access using your environment variable.</p>
</li>
<li><p>Create a database user.</p>
</li>
<li><p>Get the connection string.</p>
</li>
</ul>
<p>Replace the <code>your_mongodb_atlas_uri</code> in <code>.env</code> file with your MongoDB Atlas connection string.</p>
<p>If you are still not comfortable with how to set up MongoDB atlas, read this: <a target="_blank" href="https://www.freecodecamp.org/news/get-started-with-mongodb-atlas/">MongoDB Atlas Tutorial – How to Get Started</a>.</p>
<h3 id="heading-run-the-application">Run the Application</h3>
<p>To run the application successfully using <code>npm run dev</code>, you need to install a dependency that will start both the frontend and backend simultaneously. You can do this using <a target="_blank" href="https://www.npmjs.com/package/concurrently">concurrently</a><strong>.</strong></p>
<p>Install concurrently:</p>
<p>Open your terminal, navigate to your project root directory (<code>mern-todo-app</code>), and run:</p>
<pre><code class="lang-bash">npm install concurrently
</code></pre>
<h4 id="heading-configure-packagejson">Configure <code>package.json</code>:</h4>
<p>After installing concurrently, ensure that you have a <code>package.json</code> file in your project root directory. If it doesn't exist, create one and add the following code:</p>
<p><code>mern-todo-app/package.json</code>:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"mern-todo-app"</span>,
    <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
    <span class="hljs-attr">"private"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"scripts"</span>: {
        <span class="hljs-attr">"start"</span>: <span class="hljs-string">"cd backend &amp;&amp; npm start"</span>,
        <span class="hljs-attr">"client"</span>: <span class="hljs-string">"cd frontend &amp;&amp; npm run dev"</span>,
        <span class="hljs-attr">"dev"</span>: <span class="hljs-string">"concurrently \"npm run start\" \"npm run client\""</span>
    },
    <span class="hljs-attr">"dependencies"</span>: {
        <span class="hljs-attr">"concurrently"</span>: <span class="hljs-string">"^8.2.2"</span>
    }
}
</code></pre>
<p>This <code>package.json</code> file configures the application by defining:</p>
<ul>
<li><p>Project metadata (<code>name, version, private flag</code>).</p>
</li>
<li><p>Scripts (<code>start</code>, <code>client</code>, and <code>dev</code>) to start the backend, run the frontend, and execute both simultaneously.</p>
</li>
<li><p>Dependencies, including <code>concurrently</code>, which enables running multiple scripts in parallel.</p>
</li>
<li><p>The project is set to private to prevent accidental publishing.</p>
</li>
</ul>
<h4 id="heading-start-the-application">Start the Application</h4>
<p>Ensure everything is set up and saved, then run the following command from the project root:</p>
<pre><code class="lang-bash">npm run dev
</code></pre>
<p>If the application starts successfully, you should see messages like:</p>
<pre><code class="lang-nginx"><span class="hljs-attribute">Server</span> running <span class="hljs-literal">on</span> port <span class="hljs-number">5000</span>
MongoDB Connected
</code></pre>
<h4 id="heading-view-the-application">View the Application</h4>
<p>Open your browser and navigate to:</p>
<ul>
<li><p>Frontend (To-Do app interface)<strong>:</strong> <strong>http://localhost:5173</strong></p>
</li>
<li><p>Backend (Stored tasks in the database)<strong>:</strong> <strong>http://localhost:5000/api/tasks</strong></p>
</li>
</ul>
<p>Test the functionality by adding, editing, saving, deleting tasks, and checking off completed tasks to ensure everything works properly.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Congratulations! You have successfully built a MERN To-Do app. You can further enhance it by adding features such as time and date tracking, and deploying it to a cloud platform.</p>
<p>Feel free to copy the code or clone the <a target="_blank" href="https://github.com/nuelcas/mern-todo-app.git">GitHub</a> repository to add more functionalities and customize the styling to your preference. If you found this guide helpful, please consider sharing it and <a target="_blank" href="https://www.linkedin.com/in/casmir-onyekani/">connecting</a> with me!</p>
<p>For more learning resources:</p>
<ul>
<li><p><a target="_blank" href="https://react.dev/">React.js Docs</a></p>
</li>
<li><p><a target="_blank" href="https://www.typescriptlang.org/">TypeScript Docs</a></p>
</li>
<li><p><a target="_blank" href="https://vite.dev/">Vite Docs</a></p>
</li>
<li><p><a target="_blank" href="https://www.mongodb.com/docs/">MongoDB Docs</a></p>
</li>
<li><p><a target="_blank" href="https://nodejs.org/docs/latest/api/">Node.js Docs</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Build an AI Chat Application with the MERN Stack ]]>
                </title>
                <description>
                    <![CDATA[ AI-powered chat applications are becoming an essential part of modern web development, enabling users to interact with intelligent conversational agents. Whether for customer support, virtual assistants, or educational tools, building your own AI cha... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-an-ai-chat-application-with-the-mern-stack/</link>
                <guid isPermaLink="false">67bf99792e5bf2f98ee2466d</guid>
                
                    <category>
                        <![CDATA[ MERN Stack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Wed, 26 Feb 2025 22:45:13 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1740666306092/460598ee-3036-4395-ad92-7c8db9e70ae0.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>AI-powered chat applications are becoming an essential part of modern web development, enabling users to interact with intelligent conversational agents. Whether for customer support, virtual assistants, or educational tools, building your own AI chat app can be a rewarding and practical project. If you're interested in learning how to integrate powerful AI models into a web application, this course is perfect for you.</p>
<p>We just published a course on the <a target="_blank" href="http://freeCodeCamp.org">freeCodeCamp.org</a> YouTube channel that will teach you how to build an AI chat application using the MERN stack (MongoDB, Express.js, React, and Node.js). Developed by Harris from Coding Cleverly, this hands-on course will guide you through the entire development process. You'll work with cutting-edge AI tools like the ChatGPT API, Google’s BERT, and the DeepSeek R1 model. By the end, you'll have a fully functional AI chat app and a strong understanding of backend setup, API integration, and database management.</p>
<h3 id="heading-what-youll-learn">What You’ll Learn</h3>
<p>This course provides a structured approach to building an AI chat app from scratch. Here’s what you’ll cover:</p>
<ul>
<li><p><strong>Introduction</strong> – Get an overview of the project and the technologies used.</p>
</li>
<li><p><strong>npm init</strong> – Set up the Node.js project with the necessary dependencies.</p>
</li>
<li><p><strong>Backend Folder</strong> – Organize the backend structure for efficient development.</p>
</li>
<li><p><strong>MongoDB Integration</strong> – Learn how to connect and manage data with MongoDB.</p>
</li>
<li><p><strong>OpenAI Tokens</strong> – Set up and securely manage API keys for ChatGPT integration.</p>
</li>
<li><p><strong>MongoDB Issues</strong> – Troubleshoot common database connection issues.</p>
</li>
<li><p><strong>chat.js File</strong> – Build the core chat logic to process and respond to user messages.</p>
</li>
<li><p><strong>.env File</strong> – Manage environment variables to keep sensitive data secure.</p>
</li>
<li><p><strong>Frontend Folder</strong> – Set up the React frontend for user interaction.</p>
</li>
<li><p><strong>Testing Endpoints</strong> – Ensure your backend API is working correctly.</p>
</li>
<li><p><strong>Hugging Face Models</strong> – Integrate AI models from Hugging Face for enhanced functionality.</p>
</li>
<li><p><strong>Response Handling</strong> – Manage and format AI-generated responses.</p>
</li>
<li><p><strong>DeepSeek R1 Model</strong> – Incorporate the DeepSeek R1 model for additional AI capabilities.</p>
</li>
<li><p><strong>Results</strong> – Test and refine your AI chat application.</p>
</li>
</ul>
<h3 id="heading-why-take-this-course">Why Take This Course?</h3>
<p>This course is ideal for developers who want to gain practical experience in full-stack AI application development. You'll not only learn how to build a scalable chat application but also explore the latest AI technologies that power intelligent conversations. By working with the ChatGPT API, Google's BERT, and the DeepSeek R1 model, you’ll expand your knowledge of natural language processing and AI-driven web apps.</p>
<p>Whether you're a beginner looking to strengthen your MERN stack skills or an experienced developer eager to integrate AI into your projects, this course offers a valuable learning experience.</p>
<p>Watch the full course on the <a target="_blank" href="https://youtu.be/b-JFDTZ5e1U">freeCodeCamp.org YouTube channel</a>.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/b-JFDTZ5e1U" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Build a Full Stack Book Store App Using React, Node, MongoDB ]]>
                </title>
                <description>
                    <![CDATA[ Building fullstack web applications is one of the most valuable skills in modern web development. If you're looking for a project that can take you through the complete development process—from setting up the front end to managing a robust back end—t... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-full-stack-book-store-app-using-react-node-mongodb/</link>
                <guid isPermaLink="false">670ecbf674f5ce6cffbe5c4c</guid>
                
                    <category>
                        <![CDATA[ MERN Stack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Tue, 15 Oct 2024 20:09:26 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1729022947109/b692b74c-2ffb-476f-b91f-9da94948163f.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Building fullstack web applications is one of the most valuable skills in modern web development. If you're looking for a project that can take you through the complete development process—from setting up the front end to managing a robust back end—this is the perfect course for you.</p>
<p>We just published a course on the <a target="_blank" href="http://freeCodeCamp.org">freeCodeCamp.org</a> YouTube channel that will teach you all about building a comprehensive fullstack MERN stack bookstore website. In this course, you'll learn to work with the MERN stack, which includes <strong>MongoDB</strong> for the database, <strong>Express.js</strong> and <strong>Node.js</strong> for the backend, and <strong>React</strong> for the frontend. Along the way, you'll also integrate powerful tools like <strong>Tailwind CSS</strong> for responsive design, <strong>Redux</strong> and <strong>RTK Query Toolkit</strong> for state management, <strong>Mongoose</strong> for handling MongoDB queries, and <strong>JWT (JSON Web Tokens)</strong> for authentication.</p>
<p>The project begins with setting up a bookstore website that allows users to browse books, add them to their cart, and place orders through a cash-on-delivery system. You'll create a responsive, user-friendly frontend using React and Tailwind CSS. The course also covers important ecommerce features like inventory management, where users can add and remove books from their carts and proceed to checkout.</p>
<p>For admins, the course guides you through building a secure admin dashboard where books can be managed efficiently. Admins can log in using a secure username and password, upload new books, edit existing book details, and delete entries when necessary. This not only adds real-world complexity to your application but also teaches you how to handle user roles and permissions—an important skill in fullstack development.</p>
<h3 id="heading-key-highlights-of-the-course">Key Highlights of the Course:</h3>
<ul>
<li><p><strong>Frontend Development</strong>: You'll use <strong>React</strong> to build a dynamic, responsive user interface for your bookstore. From creating reusable components to building a navigation bar, product display sections, and a cart system, you'll cover all the essential aspects of a modern web application's front end.</p>
</li>
<li><p><strong>Backend Development</strong>: Learn to set up a <strong>Node.js</strong> server with <strong>Express.js</strong> and handle data storage with <strong>MongoDB</strong>. You'll define schemas using <strong>Mongoose</strong>, create API routes for books and orders, and integrate user authentication with <strong>JWT</strong>.</p>
</li>
<li><p><strong>State Management</strong>: Discover how to manage the application's state effectively using <strong>Redux</strong> and <strong>RTK Query Toolkit</strong>, ensuring a seamless user experience as data flows between the frontend and backend.</p>
</li>
<li><p><strong>Authentication &amp; Security</strong>: Secure the bookstore with <strong>JWT-based authentication</strong>. Implement private routes to restrict admin functionalities and ensure only authorized users can manage the inventory.</p>
</li>
<li><p><strong>Admin Dashboard</strong>: Build a full-featured admin panel for adding, editing, and deleting books. You'll learn how to manage this secure section of the site and ensure it operates smoothly.</p>
</li>
<li><p><strong>Deployment</strong>: The course concludes with guidance on deploying your MERN stack project, so you can make your bookstore live and accessible to the world.</p>
</li>
</ul>
<p>This project will help you build a fully functional MERN stack application from scratch. You'll start with setting up the project and installing essential dependencies like <strong>React Router DOM</strong> for navigation, <strong>Redux</strong> for state management, and <strong>RTK Query</strong> for data fetching. You'll then move into creating individual features like a responsive navbar, a book display system, a cart page, and a checkout system.</p>
<p>Watch the full course on <a target="_blank" href="https://www.youtube.com/watch?v=pgw2KPfgK1E">the freeCodeCamp.org YouTube channel</a> (9-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/pgw2KPfgK1E" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn the MERN Stack by Building a Store ]]>
                </title>
                <description>
                    <![CDATA[ The MERN stack is a popular method to develop full-stack web applications. We just released a new course on the freeCodeCamp.org YouTube channel that will guide you through building dynamic, responsive web applications using the popular MERN stack. T... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-the-mern-stack-by-building-a-store/</link>
                <guid isPermaLink="false">66d8d1f0d6a2e40f8e5f7fb3</guid>
                
                    <category>
                        <![CDATA[ MERN Stack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Wed, 04 Sep 2024 21:32:32 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1725485472834/b95dd94c-7118-4290-91ca-5bb9d0a0db7a.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>The MERN stack is a popular method to develop full-stack web applications.</p>
<p>We just released a new course on the freeCodeCamp.org YouTube channel that will guide you through building dynamic, responsive web applications using the popular MERN stack.</p>
<p>The MERN stack, consisting of MongoDB, Express.js, React, and Node.js, is a powerful combination of technologies that allows developers to create robust, scalable web applications. This comprehensive course, developed by Burak Orkmez, offers a hands-on approach to learning these technologies and how they work together.</p>
<p>Here is a brief overview of the core technologies:</p>
<ol>
<li><p>MongoDB: Learn to work with this flexible, document-based NoSQL database.</p>
</li>
<li><p>Express.js: Master this minimal and flexible Node.js web application framework.</p>
</li>
<li><p>React: Dive into building interactive user interfaces with this popular JavaScript library.</p>
</li>
<li><p>Node.js: Understand how to use JavaScript on the server-side with this runtime environment.</p>
</li>
</ol>
<p>The course is structured to provide a comprehensive learning experience:</p>
<ol>
<li><p>App Showcase: Get inspired by seeing the final product you'll be building.</p>
</li>
<li><p>Server Setup: Learn how to set up your Node.js server with Express.js.</p>
</li>
<li><p>MongoDB Setup: Understand how to integrate MongoDB into your application.</p>
</li>
<li><p>SQL vs NoSQL: Gain insights into the differences between SQL and NoSQL databases.</p>
</li>
<li><p>Creating a Product Model: Learn about data modeling in MongoDB.</p>
</li>
<li><p>Building the API: Develop a robust RESTful API for your application.</p>
</li>
<li><p>Frontend Development: Create an interactive user interface with React.</p>
</li>
<li><p>Deployment: Master the process of deploying your full-stack application.</p>
</li>
</ol>
<p>This course is perfect for aspiring web developers, those looking to expand their skill set, or anyone interested in modern web development practices. By the end of this course, you'll have the skills to build and deploy your own full-stack web applications using the MERN stack.</p>
<p>Watch the full course on <a target="_blank" href="https://youtu.be/O3BUHwfHf84">the freeCodeCamp.org YouTube channel</a> (2-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/O3BUHwfHf84" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
