Emdadul's BlogArticles on web development, software engineering, and technology by Emdadul Islam.https://blog.mdadul.dev/How to Deploy an AI Agent with Amazon Bedrock AgentCorehttps://blog.mdadul.dev/how-to-deploy-an-ai-agent-with-amazon-bedrock-agentcore/https://blog.mdadul.dev/how-to-deploy-an-ai-agent-with-amazon-bedrock-agentcore/Deploy AI agents with Amazon Bedrock AgentCore for scalable configuration on AWS, integrating with frameworks like LangGraph and CrewAITue, 14 Oct 2025 18:00:00 GMT<p>Amazon Bedrock AgentCore is a managed service that makes it easier to build, deploy, and operate AI agents securely at scale on AWS. It works seamlessly with frameworks like Strands Agents, LangGraph, CrewAI, and LlamaIndex, while taking care of the complex tasks such as runtime management, IAM role configuration, and observability.</p> <p>In this guide, you’ll set up your environment, create and test a simple AI agent locally, deploy it with the AgentCore starter toolkit, and invoke it through the AWS SDK.</p> <h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2> <ul> <li><p><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-prerequisites">Prerequi</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-prerequisites">sites</a></p> </li> <li><p><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-prerequisites">Step</a> <a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-1-set-up-aws-cli">1: Set U</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-prerequisites">p AWS CLI</a></p> </li> <li><p><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-1-set-up-aws-cli">Step</a> <a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-prerequisites">2: Install a</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-1-set-up-aws-cli">nd Create You</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-prerequisites">r Agent</a></p> <ul> <li><p><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-prerequisites">Cr</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-1-set-up-aws-cli">eate</a> <a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-2-install-and-create-your-agent">a re</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-1-set-up-aws-cli">quirements.txt file</a></p> </li> <li><p><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-prerequisites">Breaking</a> <a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-2-install-and-create-your-agent">Down the</a> <a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-1-set-up-aws-cli">Code</a></p> </li> </ul> </li> <li><p><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-1-set-up-aws-cli">St</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-prerequisites">ep 3: Test th</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-2-install-and-create-your-agent">e</a> <a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-create-a-requirementstxt-file">Agent L</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-1-set-up-aws-cli">ocally</a></p> </li> <li><p><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-prerequisites">Step 4: D</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-1-set-up-aws-cli">epl</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-2-install-and-create-your-agent">oy to</a> <a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-1-set-up-aws-cli">AgentCore Ru</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-prerequisites">ntime</a></p> </li> <li><p><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-prerequisites">Step</a> <a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-2-install-and-create-your-agent">5: Invok</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-1-set-up-aws-cli">e the Agent with</a> <a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-prerequisites">AWS SDK</a></p> </li> <li><p><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-prerequisites">St</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-2-install-and-create-your-agent">ep</a> <a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-create-a-requirementstxt-file">6: C</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-2-install-and-create-your-agent">le</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-1-set-up-aws-cli">an Up</a></p> </li> <li><p><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-1-set-up-aws-cli">Common Iss</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-prerequisites">ues</a></p> </li> <li><p><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-prerequisites">Conclu</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-create-a-requirementstxt-file">sion</a></p> </li> </ul> <h2 id="heading-prerequisitehttpswwwfreecodecamporgnewsdeploy-an-ai-agent-with-amazon-bedrockheading-prerequisitesshttpswwwfreecodecamporgnewsdeploy-an-ai-agent-with-amazon-bedrockheading-step-1-set-up-aws-cli"><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-prerequisites"><strong>Prerequisite</strong></a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-1-set-up-aws-cli"><strong>s</strong></a></h2> <p><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-1-set-up-aws-cli">B</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-prerequisites">efore you sta</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-1-set-up-aws-cli">rt, make sure you have:</a></p> <ul> <li><p><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-1-set-up-aws-cli">An A</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-2-install-and-create-your-agent">WS account with credentials configured.</a></p> </li> <li><p><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-2-install-and-create-your-agent">AW</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-create-a-requirementstxt-file">S CLI installed and wo</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-breaking-down-the-code">rking.</a></p> </li> <li><p><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-breaking-down-the-code">Python 3.10</a> <a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-3-test-the-agent-locally">or later installed.</a></p> </li> <li><p><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-3-test-the-agent-locally">Boto3 installed</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-4-deploy-to-agentcore-runtime">.</a></p> </li> <li><p><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-4-deploy-to-agentcore-runtime">Model access enabled in the Amazon</a> <a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-5-invoke-the-agent-with-aws-sdk">B</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-6-clean-up">e</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-5-invoke-the-agent-with-aws-sdk">drock console (f</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-step-6-clean-up">or example, A</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-common-issues">nthropic C</a><a target="_blank" href="https://www.freecodecamp.org/news/deploy-an-ai-agent-with-amazon-bedrock#heading-conclusion">laude Sonn</a>et 4.0).</p> </li> </ul> <h2 id="heading-step-1-set-up-aws-cli"><strong>Step 1: Set Up AWS CLI</strong></h2> <p>First, install the AWS CLI if you do not already have it. On Linux or macOS: <a target="_blank" href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html">AWS CLI setup guide</a>.</p> <p>Next, <a target="_blank" href="https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html">configure</a> a profile with AWS SSO:</p> <pre><code class="lang-apache"><span class="hljs-attribute">aws</span> configure sso --profile my-profile </code></pre> <p>You’ll be prompted to enter details such as:</p> <ul> <li><p><strong>SSO start URL</strong> – the URL for your AWS organization’s IAM Identity Center portal.</p> </li> <li><p><strong>SSO region</strong> – the AWS region where IAM Identity Center is configured.</p> </li> <li><p><strong>Account ID</strong> – the AWS account you want to access.</p> </li> <li><p><strong>Role name</strong> – the IAM role you want to assume within that account.</p> </li> <li><p><strong>Default region</strong> – the region that will be used when making requests.</p> </li> <li><p><strong>Default output format</strong> – for example, <code>json</code>, <code>yaml</code>, or <code>table</code>.</p> </li> </ul> <p>This creates a new profile called <code>my-profile</code> in your AWS CLI configuration, allowing you to use that identity to interact with AWS services.</p> <p>Next, you have to verify your identity. Once your profile is configured, confirm that the CLI is correctly authenticating with AWS by running:</p> <pre><code class="lang-bash">aws sts get-caller-identity --profile my-profile </code></pre> <p>This command returns details about your identity, including:</p> <ul> <li><p><strong>Account</strong> – the AWS account ID you’re authenticated against.</p> </li> <li><p><strong>UserId</strong> – the unique identifier of your IAM role or user.</p> </li> <li><p><strong>Arn</strong> – the full Amazon Resource Name (ARN) of your identity.</p> </li> </ul> <p>If the command succeeds and shows your account information, it means your profile is properly set up and ready to use with AWS SDKs, the AWS CLI, or services like Bedrock AgentCore.</p> <h2 id="heading-step-2-install-and-create-your-agent"><strong>Step 2: Install and Create Your Agent</strong></h2> <p>First, you need to set up Python virtual environment. This prevents dependency conflicts with other projects on your machine.</p> <p>Let’s create and activate a virtual environment:</p> <p>On <strong>macOS/Linux:</strong></p> <pre><code class="lang-bash">python3 -m venv .venv <span class="hljs-built_in">source</span> .venv/bin/activate </code></pre> <p>On <strong>Windows (PowerShell or CMD):</strong></p> <pre><code class="lang-python">python -m venv .venv .venv\Scripts\activate </code></pre> <ul> <li><p><code>python -m venv .venv</code> → creates a virtual environment named <code>.venv</code> in your project folder.</p> </li> <li><p><code>.venv\Scripts\activate</code> → activates the environment.</p> </li> </ul> <p>Once activated, your terminal prompt will show (.venv) at the beginning. To deactivate:</p> <pre><code class="lang-bash">deactivate </code></pre> <h3 id="heading-create-a-requirementstxt-file"><strong>Create a</strong> <code>requirements.txt</code> file</h3> <p>List the dependencies your project needs by creating a file named <code>requirements.txt</code> in the project root:</p> <pre><code class="lang-bash">bedrock-agentcore strands-agents </code></pre> <p>This makes it easy to install everything at once with:</p> <pre><code class="lang-python">pip install -r requirements.txt </code></pre> <p>Create a file called <code>my_agent.py</code> and add the following code:</p> <pre><code class="lang-python"><span class="hljs-keyword">from</span> bedrock_agentcore <span class="hljs-keyword">import</span> BedrockAgentCoreApp <span class="hljs-keyword">from</span> strands <span class="hljs-keyword">import</span> Agent app = BedrockAgentCoreApp() <span class="hljs-comment"># Create an agent with default settings</span> agent = Agent() <span class="hljs-meta">@app.entrypoint</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">invoke</span>(<span class="hljs-params">payload</span>):</span> <span class="hljs-string">"""Your AI agent function"""</span> user_message = payload.get(<span class="hljs-string">"prompt"</span>, <span class="hljs-string">"Hello! How can I help you today?"</span>) result = agent(user_message) <span class="hljs-keyword">return</span> {<span class="hljs-string">"result"</span>: result.message} <span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>: app.run() </code></pre> <h3 id="heading-breaking-down-the-code"><strong>Breaking Down the Code</strong></h3> <ul> <li><p><code>BedrockAgentCoreApp</code> – the core runtime wrapper that handles configuration, execution, and integration with AWS services.</p> </li> <li><p><code>Agent</code> – a basic agent object from the Strands library that can process and respond to prompts.</p> </li> <li><p><code>BedrockAgentCoreApp()</code> creates the container application that manages your agent’s lifecycle.</p> </li> <li><p><code>Agent()</code> initializes a simple Strands agent with default settings. In a real-world case, you can customize this with specific tools, memory, or reasoning logic.</p> </li> <li><p>The <code>@app.entrypoint</code> decorator marks this function as the callable entry point for your agent. Whenever a request is sent to the agent (via the AWS SDK, CLI, or local test), this function is invoked.</p> </li> <li><p>The agent looks for a <code>"prompt"</code> in the incoming payload.</p> </li> <li><p>If no prompt is provided, it defaults to <code>"Hello! How can I help you today?"</code>.</p> </li> <li><p>The <code>Agent</code> object then processes this input and generates a response.</p> </li> </ul> <h2 id="heading-step-3-test-the-agent-locally"><strong>Step 3: Test the Agent Locally</strong></h2> <p>Run the agent:</p> <pre><code class="lang-bash">python3 -u my_agent.py </code></pre> <p>Open another terminal and send a request:</p> <pre><code class="lang-bash">curl -X POST http://localhost:8080/invocations \ -H <span class="hljs-string">"Content-Type: application/json"</span> \ -d <span class="hljs-string">'{"prompt": "Hello!"}'</span> </code></pre> <p>If successful, you will see:</p> <pre><code class="lang-plaintext">{"result": "Hello! I'm here to help..."} </code></pre> <p>You can stop the agent with Ctrl+C.</p> <h2 id="heading-step-4-deploy-to-agentcore-runtime"><strong>Step 4: Deploy to AgentCore Runtime</strong></h2> <p>Now you are ready to deploy your agent to AWS.</p> <p>Configure the agent:</p> <pre><code class="lang-plaintext">agentcore configure -e my_agent.py </code></pre> <p>This creates a configuration file called <code>bedrock_agentcore.yaml</code>.</p> <p>You can launch the deployment with this command:</p> <pre><code class="lang-plaintext">agentcore launch </code></pre> <p>The output will include:</p> <ul> <li><p>The Amazon Resource Name (ARN) of your agent.</p> </li> <li><p>The location of logs in Amazon CloudWatch.</p> </li> </ul> <p>Test your deployed agent:</p> <pre><code class="lang-bash">agentcore invoke <span class="hljs-string">'{"prompt": "tell me a joke"}'</span> </code></pre> <p>If you get a joke back, your agent is running successfully.</p> <h2 id="heading-step-5-invoke-the-agent-with-aws-sdk"><strong>Step 5: Invoke the Agent with AWS SDK</strong></h2> <p>You can call your agent programmatically using Boto3. Create a file called <code>invoke_agent.py</code>:</p> <pre><code class="lang-python"><span class="hljs-keyword">import</span> json <span class="hljs-keyword">import</span> boto3 agent_arn = <span class="hljs-string">"YOUR_AGENT_ARN"</span> prompt = <span class="hljs-string">"Tell me a joke"</span> agent_core_client = boto3.client(<span class="hljs-string">"bedrock-agentcore"</span>) payload = json.dumps({<span class="hljs-string">"prompt"</span>: prompt}).encode() response = agent_core_client.invoke_agent_runtime( agentRuntimeArn=agent_arn, payload=payload ) content = [] <span class="hljs-keyword">for</span> chunk <span class="hljs-keyword">in</span> response.get(<span class="hljs-string">"response"</span>, []): content.append(chunk.decode(<span class="hljs-string">"utf-8"</span>)) print(json.loads(<span class="hljs-string">""</span>.join(content))) </code></pre> <p>Run the script:</p> <pre><code class="lang-plaintext">python invoke_agent.py </code></pre> <p>You should see the AI agent’s response.</p> <h2 id="heading-step-6-clean-up"><strong>Step 6: Clean Up</strong></h2> <p><strong>If you no longer want to run the agent, delete the runtime:</strong></p> <pre><code class="lang-plaintext">aws bedrock-agentcore delete-agent-runtime --agent-runtime-arn &lt;your_arn&gt; </code></pre> <h3 id="heading-common-issues"><strong>Common Issues</strong></h3> <ul> <li><p><strong>Permission denied</strong>: Check your AWS credentials and IAM policies.</p> </li> <li><p><strong>Docker warning</strong>: Ignore this unless you use — local or — local-build.</p> </li> <li><p><strong>Model access denied</strong>: Enable model access (such as Claude Sonnet 4.0) in the Bedrock console.</p> </li> <li><p><strong>Build errors</strong>: Check CloudWatch build logs and IAM policies.</p> </li> </ul> <h3 id="heading-conclusion"><strong>Conclusion</strong></h3> <p>Amazon Bedrock AgentCore makes it easy to create and deploy AI agents without dealing with complex container setups or infrastructure. You can test locally, launch to the cloud with one command, and monitor everything through CloudWatch.</p> <p>This workflow is ideal for developers who want to move from prototype to production quickly while staying inside the AWS ecosystem.</p> <p>Resources:</p> <p><a target="_blank" href="https://strandsagents.com/latest/">https://strandsagents.com/latest/</a></p> <p><a target="_blank" href="https://aws.amazon.com/bedrock/agentcore/">https://aws.amazon.com/bedrock/agentcore/</a></p> How to Add a Native Rich Text Editor in Expo / React Native (No WebView)https://blog.mdadul.dev/how-to-add-a-native-rich-text-editor-in-expo-react-native-no-webview-7538da222121/https://blog.mdadul.dev/how-to-add-a-native-rich-text-editor-in-expo-react-native-no-webview-7538da222121/Use expo-rte for a native rich text editor in Expo/React Native, offering cross-platform, customizable, native performance without WebViewTue, 12 Aug 2025 18:36:09 GMT<p>Rich text editing in React Native has always been tricky — especially when you want <strong>native performance</strong> instead of relying on WebViews. Most available libraries work great for the web, but fall short on mobile.</p> <p>That’s where <code>[expo-rte](https://github.com/mdadul/expo-rte)</code> comes in.<br /> It’s a <strong>pure native</strong> rich text editor for Expo and React Native, built using <code>expo-modules</code>, Kotlin, and Swift. No WebView. Fully cross-platform.</p> <p>In this guide, you’ll learn how to install, set up, and customize <code>expo-rte</code> in your app.</p> <h4 id="heading-why-expo-rte">Why <code>expo-rte</code>?</h4> <ul> <li><p><strong>Pure native UI</strong> — smooth scrolling, native gestures, no WebView overhead.</p> </li> <li><p><strong>Cross-platform</strong> — works on both iOS and Android.</p> </li> <li><p><strong>Feature-rich</strong> — bold, italic, underline, strikethrough, bullet lists, numbered lists, undo/redo, and more.</p> </li> <li><p><strong>Customizable</strong> — configure toolbar layout, button density, and styles.</p> </li> </ul> <p>This guide will walk you through installing and using it in your Expo or React Native project.</p> <p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771086396984/f459cff0-b024-4930-a9b4-d2033ff19ceb.gif" alt /></p> <h4 id="heading-install-expo-rte">Install <code>expo-rte</code></h4> <p>Run:</p> <p><code>npm install expo-rte</code><br /># or<br /><code>yarn add expo-rte</code></p> <p>Since it’s a native module, you’ll need to run a <strong>development build</strong> (Expo Go won’t work):</p> <p><code>npx expo run:ios</code><br /><code>npx expo run:android</code></p> <h4 id="heading-add-the-editor-to-your-app">Add the Editor to Your App</h4> <p>Create a simple editor screen:</p> <pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React, { useRef } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>; <span class="hljs-keyword">import</span> { View } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-native'</span>; <span class="hljs-keyword">import</span> { RichTextEditor, RichTextEditorRef } <span class="hljs-keyword">from</span> <span class="hljs-string">'expo-rte'</span>; <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</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> editorRef = useRef&lt;RichTextEditorRef&gt;(<span class="hljs-literal">null</span>); <span class="hljs-keyword">const</span> handleChange = <span class="hljs-function">(<span class="hljs-params">{ nativeEvent }</span>) =&gt;</span> { <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Content:'</span>, nativeEvent.content); }; <span class="hljs-keyword">return</span> ( <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">View</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">flex:</span> <span class="hljs-attr">1</span>, <span class="hljs-attr">padding:</span> <span class="hljs-attr">20</span> }}&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">RichTextEditor</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">{editorRef}</span> <span class="hljs-attr">content</span>=<span class="hljs-string">" Start typing... "</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter your text here..."</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleChange}</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">height:</span> <span class="hljs-attr">300</span> }} /&gt;</span> ); }</span> </code></pre> <h4 id="heading-customize-the-toolbar">Customize the Toolbar</h4> <p><code>expo-rte</code> lets you control which formatting options appear.</p> <pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { ToolbarConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'expo-rte'</span>; <span class="hljs-keyword">const</span> toolbarConfig: ToolbarConfig = { <span class="hljs-attr">adaptive</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">density</span>: <span class="hljs-string">'comfortable'</span>, <span class="hljs-attr">scrollable</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">groupButtons</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">buttons</span>: [ { <span class="hljs-attr">type</span>: <span class="hljs-string">'bold'</span>, <span class="hljs-attr">icon</span>: <span class="hljs-string">'B'</span>, <span class="hljs-attr">label</span>: <span class="hljs-string">'Bold'</span>, <span class="hljs-attr">group</span>: <span class="hljs-string">'format'</span> }, { <span class="hljs-attr">type</span>: <span class="hljs-string">'italic'</span>, <span class="hljs-attr">icon</span>: <span class="hljs-string">'I'</span>, <span class="hljs-attr">label</span>: <span class="hljs-string">'Italic'</span>, <span class="hljs-attr">group</span>: <span class="hljs-string">'format'</span> }, { <span class="hljs-attr">type</span>: <span class="hljs-string">'underline'</span>, <span class="hljs-attr">icon</span>: <span class="hljs-string">'U'</span>, <span class="hljs-attr">label</span>: <span class="hljs-string">'Underline'</span>, <span class="hljs-attr">group</span>: <span class="hljs-string">'format'</span> }, { <span class="hljs-attr">type</span>: <span class="hljs-string">'bullet'</span>, <span class="hljs-attr">icon</span>: <span class="hljs-string">'•'</span>, <span class="hljs-attr">label</span>: <span class="hljs-string">'Bullet List'</span>, <span class="hljs-attr">group</span>: <span class="hljs-string">'list'</span> }, { <span class="hljs-attr">type</span>: <span class="hljs-string">'numbered'</span>, <span class="hljs-attr">icon</span>: <span class="hljs-string">'1.'</span>, <span class="hljs-attr">label</span>: <span class="hljs-string">'Numbered List'</span>, <span class="hljs-attr">group</span>: <span class="hljs-string">'list'</span> }, { <span class="hljs-attr">type</span>: <span class="hljs-string">'undo'</span>, <span class="hljs-attr">icon</span>: <span class="hljs-string">'↶'</span>, <span class="hljs-attr">label</span>: <span class="hljs-string">'Undo'</span>, <span class="hljs-attr">group</span>: <span class="hljs-string">'action'</span> }, { <span class="hljs-attr">type</span>: <span class="hljs-string">'redo'</span>, <span class="hljs-attr">icon</span>: <span class="hljs-string">'↷'</span>, <span class="hljs-attr">label</span>: <span class="hljs-string">'Redo'</span>, <span class="hljs-attr">group</span>: <span class="hljs-string">'action'</span> }, ], }; </code></pre> <p>Pass it into your editor:</p> <p><code>&lt;RichTextEditor toolbarConfig={toolbarConfig} /&gt;</code></p> <h4 id="heading-control-the-editor-programmatically">Control the Editor Programmatically</h4> <p>With a <code>ref</code>, you can:</p> <pre><code class="lang-javascript">editorRef.current?.setContent(<span class="hljs-string">'&lt;p&gt;New content&lt;/p&gt;'</span>); <span class="hljs-keyword">const</span> html = <span class="hljs-keyword">await</span> editorRef.current?.getContent(); editorRef.current?.undo(); editorRef.current?.redo(); </code></pre> <h4 id="heading-troubleshooting"><strong>Troubleshooting</strong></h4> <ul> <li><p><strong>Editor not showing?</strong> Make sure you’re using a development build.</p> </li> <li><p><strong>Buttons not working?</strong> Check your <code>toolbarConfig</code>.</p> </li> <li><p><strong>Performance issues?</strong> Enable <code>adaptive: true</code> in the toolbar config.</p> </li> </ul> <p><strong>Conclusion:</strong> With <code>expo-rte</code>, you can add a <strong>native</strong> rich text editor to your Expo/React Native app—no WebView required. It’s fast, customizable, and works across iOS and Android.</p> <p>📌 Full docs &amp; source code: <a target="_blank" href="https://github.com/mdadul/expo-rte">github.com/mdadul/expo-rte</a></p> How to Implement Multi-Factor Authentication (MFA) with TOTP in Your Web Applicationhttps://blog.mdadul.dev/how-to-implement-multi-factor-authentication-mfa-with-totp-in-your-web-application-678bb5478ebf/https://blog.mdadul.dev/how-to-implement-multi-factor-authentication-mfa-with-totp-in-your-web-application-678bb5478ebf/Learn to implement Multi-Factor Authentication (MFA) with Time-based One-Time Password (TOTP) in your web application for enhanced securityMon, 07 Jul 2025 07:35:39 GMT<p>In today’s digital landscape, securing user accounts with just a password isn’t enough. Multi-Factor Authentication (MFA) adds an essential layer of security by requiring users to provide two or more verification factors. In this comprehensive guide, we’ll walk through implementing Time-based One-Time Password (TOTP) authentication in a full-stack web application.</p> <h3 id="heading-understanding-mfa-and-totp">Understanding MFA and TOTP</h3> <h4 id="heading-what-is-multi-factor-authentication">What is Multi-Factor Authentication?</h4> <p>Multi-Factor Authentication (MFA) is a security method that requires users to provide two or more verification factors to gain access to an account. These factors typically fall into three categories:</p> <ul> <li><p>Something you know (password, PIN)</p> </li> <li><p>Something you have (smartphone, hardware token)</p> </li> <li><p>Something you are (fingerprint, facial recognition)</p> </li> </ul> <h4 id="heading-what-is-totp">What is TOTP?</h4> <p>Time-based One-Time Password (TOTP) is a computer algorithm that generates a one-time password using the current time as a source of uniqueness. It’s standardized in RFC 6238 and is widely used by authenticator apps like Google Authenticator, Authy, and Microsoft Authenticator.</p> <p><strong>Key characteristics of TOTP:</strong></p> <ul> <li><p>Time-based: Codes expire every 30 seconds</p> </li> <li><p>Synchronized: Both server and client use the same time window</p> </li> <li><p>Secure: Based on HMAC-SHA1 cryptographic algorithm</p> </li> <li><p>Standardized: Works across different authenticator apps</p> </li> </ul> <h4 id="heading-setting-up-the-backend">Setting Up the Backend</h4> <p>Let’s start by setting up the necessary dependencies for our Node.js/Bun backend.</p> <h4 id="heading-dependencies">Dependencies</h4> <pre><code class="lang-json">{ <span class="hljs-attr">"dependencies"</span>: { <span class="hljs-attr">"speakeasy"</span>: <span class="hljs-string">"^2.0.0"</span>, <span class="hljs-attr">"qrcode"</span>: <span class="hljs-string">"^1.5.4"</span>, <span class="hljs-attr">"hono"</span>: <span class="hljs-string">"^4.6.3"</span>, <span class="hljs-attr">"jsonwebtoken"</span>: <span class="hljs-string">"^9.0.2"</span>, <span class="hljs-attr">"bcryptjs"</span>: <span class="hljs-string">"^2.4.3"</span> }, <span class="hljs-attr">"devDependencies"</span>: { <span class="hljs-attr">"@types/speakeasy"</span>: <span class="hljs-string">"^2.0.10"</span>, <span class="hljs-attr">"@types/qrcode"</span>: <span class="hljs-string">"^1.5.5"</span> } } </code></pre> <h4 id="heading-core-libraries-explained">Core Libraries Explained</h4> <ul> <li><p>speakeasy: The core library for TOTP generation and verification</p> </li> <li><p>qrcode: Generates QR codes that users can scan with their authenticator apps (if you’re a .NET developers, you can use <a target="_blank" href="https://ironsoftware.com/csharp/qr/"><strong>IronQr</strong></a>)</p> </li> <li><p>hono: Fast web framework (you can substitute with Express.js)</p> </li> <li><p>jsonwebtoken: For JWT token management</p> </li> <li><p>bcryptjs: For password hashing</p> </li> </ul> <h4 id="heading-totp-utility-setup">TOTP Utility Setup</h4> <p>First, let’s create a utility file to export our TOTP dependencies:</p> <pre><code class="lang-javascript"><span class="hljs-comment">// src/utils/totp.ts</span> <span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> speakeasy <span class="hljs-keyword">from</span> <span class="hljs-string">'speakeasy'</span>; <span class="hljs-keyword">import</span> QRCode <span class="hljs-keyword">from</span> <span class="hljs-string">'qrcode'</span>; <span class="hljs-keyword">export</span> { speakeasy, QRCode }; </code></pre> <h4 id="heading-database-schema">Database Schema</h4> <p>The database schema needs to support MFA by storing the TOTP secret and status for each user:</p> <pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-keyword">IF</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">EXISTS</span> <span class="hljs-keyword">users</span> ( <span class="hljs-keyword">id</span> <span class="hljs-built_in">INTEGER</span> PRIMARY <span class="hljs-keyword">KEY</span> AUTOINCREMENT, email <span class="hljs-built_in">TEXT</span> <span class="hljs-keyword">UNIQUE</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>, password_hash <span class="hljs-built_in">TEXT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>, totp_secret <span class="hljs-built_in">TEXT</span>, <span class="hljs-comment">-- Base32 encoded secret for TOTP</span> totp_enabled <span class="hljs-built_in">INTEGER</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-number">0</span>, <span class="hljs-comment">-- Boolean flag for MFA status</span> last_login <span class="hljs-built_in">TEXT</span> ); </code></pre> <h4 id="heading-key-fields-for-mfa">Key Fields for MFA</h4> <ul> <li><p>totp_secret: Stores the Base32 encoded secret used for TOTP generation</p> </li> <li><p>totp_enabled: Boolean flag indicating whether MFA is enabled for the user</p> </li> </ul> <h4 id="heading-backend-api-implementation">Backend API Implementation</h4> <p>Now let’s implement the complete MFA flow with five key endpoints:</p> <h4 id="heading-1-enhanced-login-endpoint">1. Enhanced Login Endpoint</h4> <pre><code class="lang-javascript"><span class="hljs-comment">// Enhanced login to check for MFA requirement</span> app.post(<span class="hljs-string">'/api/login'</span>, <span class="hljs-keyword">async</span> (c: any) =&gt; { <span class="hljs-keyword">const</span> ip = c.req.header(<span class="hljs-string">'x-forwarded-for'</span>) || c.req.header(<span class="hljs-string">'x-real-ip'</span>) || <span class="hljs-string">'unknown'</span>; <span class="hljs-comment">// Rate limiting</span> <span class="hljs-keyword">if</span> (rateLimit(ip, <span class="hljs-string">'login'</span>, <span class="hljs-number">10</span>, <span class="hljs-number">60</span>_000)) { <span class="hljs-keyword">return</span> c.json({ <span class="hljs-attr">error</span>: <span class="hljs-string">'Too many requests'</span> }, <span class="hljs-number">429</span>); } <span class="hljs-keyword">const</span> { email, password } = <span class="hljs-keyword">await</span> c.req.json(); <span class="hljs-comment">// Validate input</span> <span class="hljs-keyword">if</span> (!isValidEmail(email) || !isValidPassword(password)) { <span class="hljs-keyword">return</span> c.json({ <span class="hljs-attr">error</span>: <span class="hljs-string">'Invalid credentials'</span> }, <span class="hljs-number">401</span>); } <span class="hljs-keyword">const</span> user: any = db.query(<span class="hljs-string">'SELECT * FROM users WHERE email = ?'</span>).get(email); <span class="hljs-keyword">if</span> (!user) <span class="hljs-keyword">return</span> c.json({ <span class="hljs-attr">error</span>: <span class="hljs-string">'Invalid credentials'</span> }, <span class="hljs-number">401</span>); <span class="hljs-keyword">const</span> valid = <span class="hljs-keyword">await</span> verifyPassword(password, user.password_hash); <span class="hljs-keyword">if</span> (!valid) <span class="hljs-keyword">return</span> c.json({ <span class="hljs-attr">error</span>: <span class="hljs-string">'Invalid credentials'</span> }, <span class="hljs-number">401</span>); <span class="hljs-comment">// Check if MFA is enabled</span> <span class="hljs-keyword">if</span> (user.totp_enabled) { <span class="hljs-keyword">return</span> c.json({ <span class="hljs-attr">requiresTOTP</span>: <span class="hljs-literal">true</span> }); } <span class="hljs-comment">// Standard login flow for users without MFA</span> db.run(<span class="hljs-string">'UPDATE users SET last_login = ? WHERE id = ?'</span>, [<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toISOString(), user.id]); <span class="hljs-keyword">const</span> token = generateJWT(user); <span class="hljs-keyword">return</span> c.json({ token, <span class="hljs-attr">user</span>: { <span class="hljs-attr">email</span>: user.email, <span class="hljs-attr">totpEnabled</span>: !!user.totp_enabled } }); }); </code></pre> <h4 id="heading-2-mfa-login-verification">2. MFA Login Verification</h4> <pre><code class="lang-javascript"><span class="hljs-comment">// Login with 2FA verification</span> app.post(<span class="hljs-string">'/api/login-2fa'</span>, <span class="hljs-keyword">async</span> (c: any) =&gt; { <span class="hljs-keyword">const</span> ip = c.req.header(<span class="hljs-string">'x-forwarded-for'</span>) || c.req.header(<span class="hljs-string">'x-real-ip'</span>) || <span class="hljs-string">'unknown'</span>; <span class="hljs-keyword">if</span> (rateLimit(ip, <span class="hljs-string">'login2fa'</span>, <span class="hljs-number">10</span>, <span class="hljs-number">60</span>_000)) { <span class="hljs-keyword">return</span> c.json({ <span class="hljs-attr">error</span>: <span class="hljs-string">'Too many requests'</span> }, <span class="hljs-number">429</span>); } </code></pre> <h3 id="heading-3-mfa-setup-endpoint">3. MFA Setup Endpoint</h3> <pre><code class="lang-javascript"><span class="hljs-comment">// Generate MFA secret and QR code</span> app.post(<span class="hljs-string">'/api/2fa/setup'</span>, authMiddleware, <span class="hljs-keyword">async</span> (c: any) =&gt; { <span class="hljs-keyword">const</span> user = c.get(<span class="hljs-string">'user'</span>); <span class="hljs-comment">// Generate a new secret</span> <span class="hljs-keyword">const</span> secret = speakeasy.generateSecret({ <span class="hljs-attr">name</span>: <span class="hljs-string">`AuthApp:<span class="hljs-subst">${user.email}</span>`</span>, <span class="hljs-attr">issuer</span>: <span class="hljs-string">'AuthApp'</span> }); <span class="hljs-comment">// Store the secret (temporarily until verified)</span> db.run(<span class="hljs-string">'UPDATE users SET totp_secret = ? WHERE id = ?'</span>, [secret.base32, user.id]); <span class="hljs-comment">// Generate QR code data URL</span> <span class="hljs-keyword">const</span> otpauthUrl = secret.otpauth_url; <span class="hljs-keyword">const</span> qrCodeDataUrl = <span class="hljs-keyword">await</span> QRCode.toDataURL(otpauthUrl); <span class="hljs-keyword">return</span> c.json({ <span class="hljs-attr">secret</span>: secret.base32, <span class="hljs-attr">qrCode</span>: qrCodeDataUrl }); }); </code></pre> <p>For .NET developers implementing the same TOTP flow, the <a target="_blank" href="https://ironsoftware.com/suite/">Iron Suite</a> includes <a target="_blank" href="https://ironsoftware.com/csharp/qr/">IronQR</a> for generating the authenticator QR codes. The setup works similarly to the Node.js approach but integrates natively with <code>ASP.NET</code> Core or any C# backend.</p> <pre><code class="lang-csharp"><span class="hljs-keyword">using</span> IronQr; <span class="hljs-keyword">using</span> OtpNet; <span class="hljs-comment">// Generate TOTP secret</span> <span class="hljs-keyword">var</span> secret = Base32Encoding.ToString(KeyGeneration.GenerateRandomKey(<span class="hljs-number">20</span>)); <span class="hljs-keyword">var</span> otpauthUrl = <span class="hljs-string">$"otpauth://totp/YourApp:<span class="hljs-subst">{userEmail}</span>?secret=<span class="hljs-subst">{secret}</span>&amp;issuer=YourApp"</span>; <span class="hljs-comment">// Generate QR code</span> <span class="hljs-keyword">var</span> qrCode = QrWriter.Write(otpauthUrl); <span class="hljs-keyword">var</span> qrDataUrl = <span class="hljs-string">$"data:image/png;base64,<span class="hljs-subst">{Convert.ToBase64String(qrCode.SaveAsPng())}</span>"</span>; <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> { secret, qrCode = qrDataUrl }; </code></pre> <p>// Login with 2FA<br />app.post('/api/login-2fa', async (c: any) =&gt; {<br />const { email, password, totpCode } = await c.req.json();</p> <pre><code class="lang-javascript"><span class="hljs-comment">// Login with 2FA</span> app.post(<span class="hljs-string">'/api/login-2fa'</span>, <span class="hljs-keyword">async</span> (c: any) =&gt; { <span class="hljs-keyword">const</span> { email, password, totpCode } = <span class="hljs-keyword">await</span> c.req.json(); <span class="hljs-comment">// Validate credentials first</span> <span class="hljs-keyword">const</span> user: any = db.query(<span class="hljs-string">'SELECT * FROM users WHERE email = ?'</span>).get(email); <span class="hljs-keyword">if</span> (!user) <span class="hljs-keyword">return</span> c.json({ <span class="hljs-attr">error</span>: <span class="hljs-string">'Invalid credentials'</span> }, <span class="hljs-number">401</span>); <span class="hljs-keyword">const</span> valid = <span class="hljs-keyword">await</span> verifyPassword(password, user.password_hash); <span class="hljs-keyword">if</span> (!valid) <span class="hljs-keyword">return</span> c.json({ <span class="hljs-attr">error</span>: <span class="hljs-string">'Invalid credentials'</span> }, <span class="hljs-number">401</span>); <span class="hljs-comment">// Ensure MFA is enabled</span> <span class="hljs-keyword">if</span> (!user.totp_enabled || !user.totp_secret) { <span class="hljs-keyword">return</span> c.json({ <span class="hljs-attr">error</span>: <span class="hljs-string">'2FA not enabled'</span> }, <span class="hljs-number">400</span>); } <span class="hljs-comment">// Verify TOTP code</span> <span class="hljs-keyword">const</span> verified = speakeasy.totp.verify({ <span class="hljs-attr">secret</span>: user.totp_secret, <span class="hljs-attr">encoding</span>: <span class="hljs-string">'base32'</span>, <span class="hljs-attr">token</span>: totpCode, <span class="hljs-attr">window</span>: <span class="hljs-number">1</span> <span class="hljs-comment">// Allow ±30 seconds time drift</span> }); <span class="hljs-keyword">if</span> (!verified) { <span class="hljs-keyword">return</span> c.json({ <span class="hljs-attr">error</span>: <span class="hljs-string">'Invalid 2FA code'</span> }, <span class="hljs-number">401</span>); } <span class="hljs-comment">// Successful login</span> db.run(<span class="hljs-string">'UPDATE users SET last_login = ? WHERE id = ?'</span>, [<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toISOString(), user.id]); <span class="hljs-keyword">const</span> token = generateJWT(user); <span class="hljs-keyword">return</span> c.json({ token, <span class="hljs-attr">user</span>: { <span class="hljs-attr">email</span>: user.email, <span class="hljs-attr">totpEnabled</span>: !!user.totp_enabled } }); }); </code></pre> <p><em>.NET (C#) — TOTP Verification:</em></p> <pre><code class="lang-csharp"><span class="hljs-keyword">using</span> OtpNet; <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">VerifyTotp</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> base32Secret, <span class="hljs-keyword">string</span> totpCode</span>)</span> { <span class="hljs-comment">// Decode Base32 secret (same as encoding: 'base32' in speakeasy)</span> <span class="hljs-keyword">var</span> secretBytes = Base32Encoding.ToBytes(base32Secret); <span class="hljs-comment">// Create TOTP instance (30s step, 6 digits, SHA1)</span> <span class="hljs-keyword">var</span> totp = <span class="hljs-keyword">new</span> Totp(secretBytes); <span class="hljs-comment">// Verify with ±30s window (same as window: 1)</span> <span class="hljs-keyword">bool</span> verified = totp.VerifyTotp( totpCode, <span class="hljs-keyword">out</span> <span class="hljs-keyword">long</span> timeStepMatched, <span class="hljs-keyword">new</span> VerificationWindow(previous: <span class="hljs-number">1</span>, future: <span class="hljs-number">1</span>) ); <span class="hljs-keyword">return</span> verified; } </code></pre> <h4 id="heading-4-mfa-verification-and-activation">4. MFA Verification and Activation</h4> <pre><code class="lang-csharp"><span class="hljs-comment">// Verify TOTP code and enable MFA</span> app.post(<span class="hljs-string">'/api/2fa/verify'</span>, authMiddleware, <span class="hljs-keyword">async</span> (c: any) =&gt; { <span class="hljs-keyword">const</span> user = c.<span class="hljs-keyword">get</span>(<span class="hljs-string">'user'</span>); <span class="hljs-keyword">const</span> { token } = <span class="hljs-keyword">await</span> c.req.json(); <span class="hljs-keyword">const</span> dbUser: any = db.query(<span class="hljs-string">'SELECT * FROM users WHERE id = ?'</span>).<span class="hljs-keyword">get</span>(user.id); <span class="hljs-keyword">if</span> (!dbUser || !dbUser.totp_secret) { <span class="hljs-keyword">return</span> c.json({ error: <span class="hljs-string">'No 2FA setup in progress'</span> }, <span class="hljs-number">400</span>); } <span class="hljs-comment">// Verify the provided TOTP code</span> <span class="hljs-keyword">const</span> verified = speakeasy.totp.verify({ secret: dbUser.totp_secret, encoding: <span class="hljs-string">'base32'</span>, token, window: <span class="hljs-number">1</span> }); <span class="hljs-keyword">if</span> (!verified) { <span class="hljs-keyword">return</span> c.json({ error: <span class="hljs-string">'Invalid code'</span> }, <span class="hljs-number">400</span>); } <span class="hljs-comment">// Enable MFA for the user</span> db.run(<span class="hljs-string">'UPDATE users SET totp_enabled = 1 WHERE id = ?'</span>, [user.id]); <span class="hljs-keyword">return</span> c.json({ success: <span class="hljs-literal">true</span> }); }); </code></pre> <h4 id="heading-5-mfa-disable-endpoint">5. MFA Disable Endpoint</h4> <pre><code class="lang-javascript"><span class="hljs-comment">// Disable MFA for user</span> app.post(<span class="hljs-string">'/api/2fa/disable'</span>, authMiddleware, <span class="hljs-keyword">async</span> (c: any) =&gt; { <span class="hljs-keyword">const</span> user = c.get(<span class="hljs-string">'user'</span>); <span class="hljs-comment">// Clear MFA settings</span> db.run(<span class="hljs-string">'UPDATE users SET totp_enabled = 0, totp_secret = NULL WHERE id = ?'</span>, [user.id]); <span class="hljs-keyword">return</span> c.json({ <span class="hljs-attr">success</span>: <span class="hljs-literal">true</span> }); }); </code></pre> <h4 id="heading-frontend-implementation">Frontend Implementation</h4> <p>Now let’s implement the frontend components to handle the MFA flow.</p> <h4 id="heading-1-authentication-api-client">1. Authentication API Client</h4> <pre><code class="lang-javascript"><span class="hljs-comment">// src/api/auth.ts</span> <span class="hljs-keyword">export</span> interface LoginResponse { token?: string; requiresTOTP?: boolean; totpEnabled?: boolean; error?: string; } <span class="hljs-keyword">export</span> interface MFASetupResponse { <span class="hljs-attr">secret</span>: string; qrCode: string; } <span class="hljs-keyword">const</span> getBaseUrl = <span class="hljs-function">() =&gt;</span> { <span class="hljs-keyword">return</span> <span class="hljs-keyword">import</span>.meta.env.VITE_API_BASE_URL || <span class="hljs-string">'http://localhost:3001/api'</span>; }; <span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">login</span>(<span class="hljs-params">email: string, password: string</span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">LoginResponse</span>&gt; </span>{ <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">`<span class="hljs-subst">${getBaseUrl()}</span>/login`</span>, { <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>, <span class="hljs-attr">headers</span>: { <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span> }, <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({ email, password }) }); <span class="hljs-keyword">return</span> res.json(); } <span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">verifyTOTP</span>(<span class="hljs-params">email: string, password: string, totpCode: string</span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">LoginResponse</span>&gt; </span>{ <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">`<span class="hljs-subst">${getBaseUrl()}</span>/login-2fa`</span>, { <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>, <span class="hljs-attr">headers</span>: { <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span> }, <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({ email, password, totpCode }) }); <span class="hljs-keyword">return</span> res.json(); } <span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setupMFA</span>(<span class="hljs-params">token: string</span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">MFASetupResponse</span>&gt; </span>{ <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">`<span class="hljs-subst">${getBaseUrl()}</span>/2fa/setup`</span>, { <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>, <span class="hljs-attr">headers</span>: { <span class="hljs-string">'Authorization'</span>: <span class="hljs-string">`Bearer <span class="hljs-subst">${token}</span>`</span> } }); <span class="hljs-keyword">return</span> res.json(); } <span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">verifyMFA</span>(<span class="hljs-params">token: string, totpCode: string</span>): <span class="hljs-title">Promise</span>&lt;</span>{ success: boolean }&gt; { <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">`<span class="hljs-subst">${getBaseUrl()}</span>/2fa/verify`</span>, { <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>, <span class="hljs-attr">headers</span>: { <span class="hljs-string">'Authorization'</span>: <span class="hljs-string">`Bearer <span class="hljs-subst">${token}</span>`</span>, <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span> }, <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({ <span class="hljs-attr">token</span>: totpCode }) }); <span class="hljs-keyword">return</span> res.json(); } </code></pre> <h4 id="heading-2-enhanced-login-component">2. Enhanced Login Component</h4> <pre><code class="lang-javascript"><span class="hljs-comment">// Enhanced login flow with MFA detection</span> <span class="hljs-keyword">const</span> handleLogin = <span class="hljs-keyword">async</span> (e: React.FormEvent) =&gt; { e.preventDefault(); setError(<span class="hljs-string">''</span>); 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> login(email, password); <span class="hljs-keyword">if</span> (response.requiresTOTP) { <span class="hljs-comment">// Store credentials temporarily for 2FA verification</span> sessionStorage.setItem(<span class="hljs-string">'loginEmail'</span>, email); sessionStorage.setItem(<span class="hljs-string">'loginPassword'</span>, password); navigate(<span class="hljs-string">'/verify'</span>); <span class="hljs-comment">// Redirect to 2FA verification page</span> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (response.token) { setToken(response.token); navigate(<span class="hljs-string">'/dashboard'</span>); } <span class="hljs-keyword">else</span> { setError(response.error || <span class="hljs-string">'Login failed'</span>); } } <span class="hljs-keyword">catch</span> (error) { setError(<span class="hljs-string">'Network error'</span>); } <span class="hljs-keyword">finally</span> { setLoading(<span class="hljs-literal">false</span>); } }; </code></pre> <h4 id="heading-3-totp-verification-component">3. TOTP Verification Component</h4> <pre><code class="lang-javascript"><span class="hljs-comment">// src/pages/Verify.tsx</span> <span class="hljs-keyword">const</span> Verify = <span class="hljs-function">() =&gt;</span> { <span class="hljs-keyword">const</span> [totpCode, setTotpCode] = useState(<span class="hljs-string">''</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> { setToken } = useAuth(); <span class="hljs-keyword">const</span> email = sessionStorage.getItem(<span class="hljs-string">'loginEmail'</span>) || <span class="hljs-string">''</span>; <span class="hljs-keyword">const</span> password = sessionStorage.getItem(<span class="hljs-string">'loginPassword'</span>) || <span class="hljs-string">''</span>; <span class="hljs-keyword">const</span> mutation = useMutation({ <span class="hljs-attr">mutationFn</span>: <span class="hljs-function">() =&gt;</span> verifyTOTP(email, password, totpCode), <span class="hljs-attr">onSuccess</span>: <span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> { <span class="hljs-keyword">if</span> (data.token) { setToken(data.token); <span class="hljs-comment">// Clean up stored credentials</span> sessionStorage.removeItem(<span class="hljs-string">'loginEmail'</span>); sessionStorage.removeItem(<span class="hljs-string">'loginPassword'</span>); navigate(<span class="hljs-string">'/dashboard'</span>); } <span class="hljs-keyword">else</span> { setError(data.error || <span class="hljs-string">'2FA failed'</span>); } }, <span class="hljs-attr">onError</span>: <span class="hljs-function">() =&gt;</span> setError(<span class="hljs-string">'Network error'</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">"verification-container"</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Two-Factor Verification<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Enter the 6-digit code from your authenticator app<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{handleVerify}</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">{totpCode}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{e</span> =&gt;</span> setTotpCode(e.target.value)} required maxLength={6} pattern="[0-9]{6}" placeholder="123456" className="totp-input" /&gt; <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">disabled</span>=<span class="hljs-string">{mutation.isPending}</span>&gt;</span> {mutation.isPending ? 'Verifying...' : 'Verify &amp; Login'} <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span> {error &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"error"</span>&gt;</span>{error}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>} <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span> ); }; </code></pre> <h4 id="heading-4-mfa-setup-modal-component">4. MFA Setup Modal Component</h4> <pre><code class="lang-javascript"><span class="hljs-comment">// MFA Setup component with QR code display</span> <span class="hljs-keyword">const</span> MFASetupModal = <span class="hljs-function">(<span class="hljs-params">{ isOpen, onClose, token }</span>) =&gt;</span> { <span class="hljs-keyword">const</span> [qrCode, setQrCode] = useState(<span class="hljs-string">''</span>); <span class="hljs-keyword">const</span> [totpCode, setTotpCode] = useState(<span class="hljs-string">''</span>); <span class="hljs-keyword">const</span> [error, setError] = useState(<span class="hljs-string">''</span>); <span class="hljs-keyword">const</span> setupMutation = useMutation({ <span class="hljs-attr">mutationFn</span>: <span class="hljs-function">() =&gt;</span> setupMFA(token), <span class="hljs-attr">onSuccess</span>: <span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> { setQrCode(data.qrCode); setError(<span class="hljs-string">''</span>); }, <span class="hljs-attr">onError</span>: <span class="hljs-function">() =&gt;</span> setError(<span class="hljs-string">'Failed to setup MFA'</span>) }); <span class="hljs-keyword">const</span> verifyMutation = useMutation({ <span class="hljs-attr">mutationFn</span>: <span class="hljs-function">() =&gt;</span> verifyMFA(token, totpCode), <span class="hljs-attr">onSuccess</span>: <span class="hljs-function">() =&gt;</span> { onClose(); <span class="hljs-comment">// Refresh user data to show MFA as enabled</span> }, <span class="hljs-attr">onError</span>: <span class="hljs-function">() =&gt;</span> setError(<span class="hljs-string">'Invalid verification code'</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">{</span>`<span class="hljs-attr">modal</span> ${<span class="hljs-attr">isOpen</span> ? '<span class="hljs-attr">open</span>' <span class="hljs-attr">:</span> ''}`}&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"modal-content"</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Setup Multi-Factor Authentication<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span> {!qrCode ? ( <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setupMutation.mutate()}&gt; {setupMutation.isPending ? 'Setting up...' : 'Start MFA Setup'} <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;<span class="hljs-name">p</span>&gt;</span>Scan this QR code with your authenticator app:<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{qrCode}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"MFA QR Code"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"qr-code"</span> /&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{handleVerify}</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">{totpCode}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{e</span> =&gt;</span> setTotpCode(e.target.value)} placeholder="Enter verification code" maxLength={6} pattern="[0-9]{6}" required /&gt; <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">disabled</span>=<span class="hljs-string">{verifyMutation.isPending}</span>&gt;</span> {verifyMutation.isPending ? 'Verifying...' : 'Verify &amp; Enable MFA'} <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span> <span class="hljs-tag">&lt;/&gt;</span> )} {error &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"error"</span>&gt;</span>{error}<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">{onClose}</span>&gt;</span>Close<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> &lt;/div&gt; ); }; </code></pre> <h4 id="heading-security-considerations">Security Considerations</h4> <h4 id="heading-1-secret-storage">1. Secret Storage</h4> <ul> <li><p>Never expose secrets: TOTP secrets should never be sent to the frontend after initial setup</p> </li> <li><p>Secure storage: Use proper database encryption for storing TOTP secrets</p> </li> <li><p>Environment isolation: Keep secrets separate from other application data</p> </li> </ul> <h4 id="heading-2-rate-limiting">2. Rate Limiting</h4> <p>Implement aggressive rate limiting on MFA endpoints:</p> <pre><code class="lang-javascript"><span class="hljs-comment">// Example rate limiting configuration</span> <span class="hljs-keyword">const</span> rateLimits = { <span class="hljs-attr">login</span>: { <span class="hljs-attr">attempts</span>: <span class="hljs-number">10</span>, <span class="hljs-attr">window</span>: <span class="hljs-number">60000</span> }, <span class="hljs-comment">// 10 attempts per minute</span> <span class="hljs-attr">login2fa</span>: { <span class="hljs-attr">attempts</span>: <span class="hljs-number">5</span>, <span class="hljs-attr">window</span>: <span class="hljs-number">60000</span> }, <span class="hljs-comment">// 5 attempts per minute</span> <span class="hljs-attr">mfaSetup</span>: { <span class="hljs-attr">attempts</span>: <span class="hljs-number">3</span>, <span class="hljs-attr">window</span>: <span class="hljs-number">3600000</span> } <span class="hljs-comment">// 3 attempts per hour</span> }; </code></pre> <h4 id="heading-3-time-window-management">3. Time Window Management</h4> <ul> <li><p>Clock skew tolerance: Use a window of ±1 period (30 seconds) to account for clock differences</p> </li> <li><p>Prevent replay attacks: Consider implementing used token tracking for high-security applications</p> </li> </ul> <h4 id="heading-4-backup-codes">4. Backup Codes</h4> <p>Consider implementing backup codes for account recovery:</p> <pre><code class="lang-javascript"><span class="hljs-comment">// Generate backup codes during MFA setup</span> <span class="hljs-keyword">const</span> generateBackupCodes = <span class="hljs-function">() =&gt;</span> { <span class="hljs-keyword">const</span> codes = []; <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10</span>; i++) { codes.push(crypto.randomBytes(<span class="hljs-number">4</span>).toString(<span class="hljs-string">'hex'</span>).toUpperCase()); } <span class="hljs-keyword">return</span> codes; }; </code></pre> <h4 id="heading-5-session-management">5. Session Management</h4> <ul> <li><p>Clear temporary data: Remove stored credentials after successful 2FA verification</p> </li> <li><p>Session timeout: Implement shorter session timeouts for MFA-enabled accounts</p> </li> <li><p>Device management: Consider implementing trusted device functionality</p> </li> </ul> <h4 id="heading-testing-your-implementation">Testing Your Implementation</h4> <h4 id="heading-1-unit-tests-for-totp-functions">1. Unit Tests for TOTP Functions</h4> <pre><code class="lang-javascript"><span class="hljs-comment">// tests/unit/totp.test.ts</span> <span class="hljs-keyword">import</span> { describe, it, expect } <span class="hljs-keyword">from</span> <span class="hljs-string">'bun:test'</span>; <span class="hljs-keyword">import</span> { speakeasy } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../src/utils/totp'</span>; describe(<span class="hljs-string">'TOTP Functions'</span>, <span class="hljs-function">() =&gt;</span> { it(<span class="hljs-string">'should generate and verify TOTP codes'</span>, <span class="hljs-function">() =&gt;</span> { <span class="hljs-keyword">const</span> secret = speakeasy.generateSecret(); <span class="hljs-keyword">const</span> token = speakeasy.totp({ <span class="hljs-attr">secret</span>: secret.base32, <span class="hljs-attr">encoding</span>: <span class="hljs-string">'base32'</span> }); <span class="hljs-keyword">const</span> verified = speakeasy.totp.verify({ <span class="hljs-attr">secret</span>: secret.base32, <span class="hljs-attr">encoding</span>: <span class="hljs-string">'base32'</span>, token, <span class="hljs-attr">window</span>: <span class="hljs-number">1</span> }); expect(verified).toBe(<span class="hljs-literal">true</span>); }); it(<span class="hljs-string">'should reject invalid TOTP codes'</span>, <span class="hljs-function">() =&gt;</span> { <span class="hljs-keyword">const</span> secret = speakeasy.generateSecret(); <span class="hljs-keyword">const</span> verified = speakeasy.totp.verify({ <span class="hljs-attr">secret</span>: secret.base32, <span class="hljs-attr">encoding</span>: <span class="hljs-string">'base32'</span>, <span class="hljs-attr">token</span>: <span class="hljs-string">'000000'</span>, <span class="hljs-attr">window</span>: <span class="hljs-number">1</span> }); expect(verified).toBe(<span class="hljs-literal">false</span>); }); }); </code></pre> <h4 id="heading-2-integration-tests">2. Integration Tests</h4> <pre><code class="lang-javascript"><span class="hljs-comment">// tests/integration/mfa.test.ts</span> <span class="hljs-keyword">import</span> { describe, it, expect, beforeAll, afterAll } <span class="hljs-keyword">from</span> <span class="hljs-string">'bun:test'</span>; <span class="hljs-keyword">import</span> { app } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../src/app'</span>; describe(<span class="hljs-string">'MFA Integration Tests'</span>, <span class="hljs-function">() =&gt;</span> { <span class="hljs-keyword">let</span> userToken: string; beforeAll(<span class="hljs-keyword">async</span> () =&gt; { <span class="hljs-comment">// Setup test user and login</span> <span class="hljs-keyword">const</span> loginResponse = <span class="hljs-keyword">await</span> app.request(<span class="hljs-string">'/api/login'</span>, { <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>, <span class="hljs-attr">headers</span>: { <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span> }, <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({ <span class="hljs-attr">email</span>: <span class="hljs-string">'[email protected]'</span>, <span class="hljs-attr">password</span>: <span class="hljs-string">'password123'</span> }) }); <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> loginResponse.json(); userToken = data.token; }); it(<span class="hljs-string">'should setup MFA and return QR code'</span>, <span class="hljs-keyword">async</span> () =&gt; { <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> app.request(<span class="hljs-string">'/api/2fa/setup'</span>, { <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>, <span class="hljs-attr">headers</span>: { <span class="hljs-string">'Authorization'</span>: <span class="hljs-string">`Bearer <span class="hljs-subst">${userToken}</span>`</span> } }); expect(response.status).toBe(<span class="hljs-number">200</span>); <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> response.json(); expect(data.secret).toBeDefined(); expect(data.qrCode).toBeDefined(); expect(data.qrCode).toMatch(<span class="hljs-regexp">/^data:image\/png;base64,/</span>); }); }); </code></pre> <h3 id="heading-3-manual-testing-checklist">3. Manual Testing Checklist</h3> <p>✅ Setup Flow</p> <ul> <li><p>[ ] QR code generation works</p> </li> <li><p>[ ] QR code scans correctly in authenticator apps</p> </li> <li><p>[ ] TOTP verification during setup works</p> </li> <li><p>[ ] MFA status updates correctly</p> </li> </ul> <p>✅ Login Flow</p> <ul> <li><p>[ ] Regular login works without MFA</p> </li> <li><p>[ ] Login with MFA enabled prompts for 2FA</p> </li> <li><p>[ ] Invalid TOTP codes are rejected</p> </li> <li><p>[ ] Valid TOTP codes allow login</p> </li> <li><p>[ ] Rate limiting prevents brute force</p> </li> </ul> <p>✅ Edge Cases</p> <ul> <li><p>[ ] Clock skew tolerance works</p> </li> <li><p>[ ] Network interruptions are handled gracefully</p> </li> <li><p>[ ] Invalid QR codes don’t break the app</p> </li> <li><p>[ ] Disabling MFA works correctly</p> </li> </ul> <p><strong>Source Code:</strong> <a target="_blank" href="https://github.com/emdadulislam1/auth-app"><strong>https://github.com/emdadulislam1/auth-app</strong></a></p> <h4 id="heading-conclusion">Conclusion</h4> <p>Implementing TOTP-based MFA significantly enhances your application’s security posture. The implementation we’ve covered includes:</p> <h4 id="heading-key-benefits-achieved">Key Benefits Achieved</h4> <ul> <li><p>Enhanced Security: Users’ accounts are protected even if passwords are compromised</p> </li> <li><p>Industry Standard: Uses RFC 6238 TOTP standard, compatible with all major authenticator apps</p> </li> <li><p>User-Friendly: Simple setup process with QR code scanning</p> </li> <li><p>Scalable: Can handle multiple users and devices efficiently</p> </li> </ul> <h4 id="heading-next-steps">Next Steps</h4> <p>Consider these enhancements for production applications:</p> <ol> <li><p>Backup Codes: Implement recovery codes for when users lose their devices</p> </li> <li><p>SMS Fallback: Add SMS-based 2FA as a backup option</p> </li> <li><p>Device Management: Allow users to manage trusted devices</p> </li> <li><p>Admin Controls: Add administrative controls for MFA policies</p> </li> <li><p>Audit Logging: Log all MFA events for security monitoring</p> </li> </ol> <h4 id="heading-security-reminders">Security Reminders</h4> <ul> <li><p>Always use HTTPS in production</p> </li> <li><p>Implement proper rate limiting</p> </li> <li><p>Store secrets securely</p> </li> <li><p>Monitor for suspicious authentication patterns</p> </li> <li><p>Keep dependencies updated</p> </li> </ul> <p>By following this guide, you’ve implemented a robust, secure, and user-friendly MFA system that follows industry best practices. Your users can now enjoy enhanced account security while maintaining a smooth authentication experience.</p> <p><em>Remember: Security is an ongoing process. Regularly review and update your implementation to address new threats and vulnerabilities.</em></p> Host Your Own S3-Compatible MinIO Server on a VPS with Caddy and HTTPShttps://blog.mdadul.dev/host-your-own-s3-compatible-minio-server-on-a-vps-with-caddy-and-https-ab8cdd8ecb34/https://blog.mdadul.dev/host-your-own-s3-compatible-minio-server-on-a-vps-with-caddy-and-https-ab8cdd8ecb34/Learn to set up a fast, secure S3-compatible MinIO server on your VPS with Caddy and HTTPS, featuring a custom subdomainSun, 20 Apr 2025 12:25:27 GMT<p>Host Your Own S3-Compatible MinIO Server on a VPS with Caddy and HTTPS</p> <p>Want to self-host object storage like AWS S3 but on your own VPS? Say hello to <strong>MinIO</strong> — a blazing-fast, S3-compatible storage solution. In this guide, we’ll show you how to install and secure MinIO using <strong>Caddy</strong> with automatic HTTPS, and host it under a custom <strong>subdomain</strong>.</p> <h4 id="heading-what-youll-need"><strong>What You’ll Need</strong></h4> <ul> <li><p>A fresh Linux VPS (Ubuntu 20.04+ recommended)</p> </li> <li><p>Root access or a sudo user</p> </li> <li><p>A domain with DNS control</p> </li> <li><p>A subdomain (e.g., <code>storage.yourdomain.com</code>) pointing to your VPS IP</p> </li> </ul> <h3 id="heading-step-1-install-minio"><strong>Step 1: Install MinIO</strong></h3> <ol> <li>Create a <code>minio</code> user:</li> </ol> <p>sudo useradd -r minio-user -s /sbin/nologin</p> <p>2. Download and install MinIO:</p> <pre><code class="lang-bash">wget https://dl.min.io/server/minio/release/linux-amd64/minio chmod +x minio sudo mv minio /usr/<span class="hljs-built_in">local</span>/bin/ </code></pre> <p>3. Create directories:</p> <pre><code class="lang-bash">sudo mkdir -p /usr/<span class="hljs-built_in">local</span>/share/minio sudo mkdir -p /etc/minio sudo chown -R minio-user:minio-user /usr/<span class="hljs-built_in">local</span>/share/minio /etc/minio </code></pre> <p>4. Create environment file:</p> <p><code>sudo nano /etc/minio/minio.env</code></p> <p>Add:</p> <p><code>MINIO_ROOT_USER=admin MINIO_ROOT_PASSWORD=strongpassword</code></p> <h3 id="heading-step-2-setup-minio-systemd-service"><strong>Step 2: Setup MinIO Systemd Service</strong></h3> <p><code>sudo nano /etc/systemd/system/minio.service</code></p> <p>paste:</p> <pre><code class="lang-bash">[Unit] Description=MinIO After=network-online.target [Service] User=minio-user Group=minio-user EnvironmentFile=/etc/minio/minio.env ExecStart=/usr/<span class="hljs-built_in">local</span>/bin/minio server /usr/<span class="hljs-built_in">local</span>/share/minio --console-address <span class="hljs-string">":9001"</span> Restart=always LimitNOFILE=65536 [Install] WantedBy=multi-user.target </code></pre> <p>Then enable and start:</p> <pre><code class="lang-bash">sudo systemctl daemon-reexec sudo systemctl <span class="hljs-built_in">enable</span> minio sudo systemctl start minio </code></pre> <h3 id="heading-step-3-point-your-subdomain"><strong>Step 3: Point Your Subdomain</strong></h3> <p>Go to your domain DNS provider and create an <code>A</code> record:</p> <p><code>storage.yourdomain.com</code> → <code>your VPS IP</code></p> <p>Wait a few minutes for it to propagate.</p> <h3 id="heading-step-4-install-amp-configure-caddy-with-https"><strong>Step 4: Install &amp; Configure Caddy (with HTTPS)</strong></h3> <ol> <li><strong>Install Caddy:</strong></li> </ol> <pre><code class="lang-bash">sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl -1sLf <span class="hljs-string">'https://dl.cloudsmith.io/public/caddy/stable/gpg.key'</span> | sudo tee /etc/apt/trusted.gpg.d/caddy-stable.asc curl -1sLf <span class="hljs-string">'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt'</span> | sudo tee /etc/apt/sources.list.d/caddy-stable.list sudo apt update sudo apt install caddy </code></pre> <p>2. Edit Caddyfile:</p> <p><code>sudo nano /etc/caddy/Caddyfile</code></p> <p>Paste:</p> <pre><code class="lang-nginx">storage.yourdomain.<span class="hljs-section">com</span> { <span class="hljs-attribute">handle_path</span> /minio/* { <span class="hljs-attribute">reverse_proxy</span> localhost:<span class="hljs-number">9000</span> } handle_path /* { <span class="hljs-attribute">reverse_proxy</span> localhost:<span class="hljs-number">9001</span> } } </code></pre> <blockquote> <p>Replace <code>yourdomain.com</code> with your actual domain.</p> </blockquote> <p>3. Restart Caddy:</p> <p><code>sudo systemctl restart caddy</code></p> <p>Caddy will automatically fetch SSL certificates and handle HTTP→HTTPS redirects 🎉</p> <h3 id="heading-step-5-test-it">Step 5: Test It!</h3> <ul> <li><p>Visit: <code>[https://storage.yourdomain.com](https://storage.yourdomain.com)</code></p> </li> <li><p>Login with the credentials you set in <code>/etc/minio/minio.env</code></p> </li> </ul> <h3 id="heading-you-did-it">You Did It!</h3> <p>You now have your own secure, S3-compatible object storage running on your VPS with:</p> <ul> <li><p><strong>MinIO</strong> as the storage engine</p> </li> <li><p><strong>Caddy</strong> for reverse proxy and HTTPS</p> </li> <li><p><strong>Your custom domain</strong> for easy access</p> </li> </ul> How to Add TanStack Query in Astro + React Projecthttps://blog.mdadul.dev/how-to-add-tanstack-query-in-astro-react-project-60fd48a48c6c/https://blog.mdadul.dev/how-to-add-tanstack-query-in-astro-react-project-60fd48a48c6c/Learn how to integrate TanStack Query in your Astro + React project for efficient data fetching and state management with practical examplesTue, 01 Oct 2024 05:15:33 GMT<p>Photo by <a target="_blank" href="https://unsplash.com/@lautaroandreani?utm_source=medium&amp;utm_medium=referral">Lautaro Andreani</a> on <a target="_blank" href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></p> <p>TanStack Query (formerly known as React Query) is a powerful tool for data fetching, caching, and synchronization in React applications. It simplifies handling server-side state in a predictable and efficient manner. Integrating TanStack Query into an Astro project with React can offer the best of both worlds — Astro’s optimized static site generation with React’s interactivity.</p> <p>In this blog, I’ll walk you through how to add TanStack Query to your Astro + React project. This process involves setting up the QueryClient in your project and using hooks like <code>useMutation</code> and <code>useQuery</code> to manage data fetching and mutations.</p> <h3 id="heading-prerequisites">Prerequisites</h3> <ul> <li><p>Astro installed in your project.</p> </li> <li><p>React set up in Astro.</p> </li> <li><p>TanStack Query installed (<code>@tanstack/react-query</code>).</p> </li> </ul> <p>If you haven’t installed TanStack Query yet, you can install it using npm or yarn:</p> <p><code>npm install @tanstack/react-query</code></p> <p>Or with Yarn:</p> <p><code>yarn add @tanstack/react-query</code></p> <h3 id="heading-1-set-up-query-client">1. Set Up Query Client</h3> <p>To integrate TanStack Query, we first need to initialize the <code>QueryClient</code>. This is a central part of the library and is responsible for managing all queries and mutations.</p> <p>Let’s create a <code>store/index.ts</code> file that will export a <code>queryClient</code>.</p> <pre><code class="lang-javascript"><span class="hljs-comment">// store/index.ts</span> <span class="hljs-keyword">import</span> { QueryClient } <span class="hljs-keyword">from</span> <span class="hljs-string">"@tanstack/react-query"</span>; <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> queryClient = <span class="hljs-keyword">new</span> QueryClient(); </code></pre> <p>Here, we simply create a new instance of <code>QueryClient</code>, which will be used across different components in our React application.</p> <h3 id="heading-2-using-usemutation-for-data-submissions">2. Using <code>useMutation</code> for Data Submissions</h3> <p>In TanStack Query, <code>useMutation</code> is used to submit or mutate data to the server. For example, let’s consider a form submission where a user registers students. Here is how you can handle a form submission using <code>useMutation</code>.</p> <pre><code class="lang-javascript"><span class="hljs-comment">// any-component.ts</span> <span class="hljs-keyword">import</span> { useMutation } <span class="hljs-keyword">from</span> <span class="hljs-string">"@tanstack/react-query"</span>; <span class="hljs-keyword">import</span> { toast } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-hot-toast"</span>; <span class="hljs-keyword">import</span> { queryClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'../store'</span>; <span class="hljs-keyword">const</span> submitFormAPI = <span class="hljs-keyword">async</span> (formData) =&gt; { <span class="hljs-comment">// API call to submit the form</span> }; <span class="hljs-keyword">const</span> mutation = useMutation({ <span class="hljs-attr">mutationFn</span>: submitFormAPI, <span class="hljs-attr">onSuccess</span>: <span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> { toast.success(<span class="hljs-string">"Registration Successful"</span>, { <span class="hljs-attr">description</span>: <span class="hljs-string">"The student has been registered successfully."</span>, }); <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Submitted data:"</span>, data); form.reset(); }, <span class="hljs-attr">onError</span>: <span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> { toast.error(<span class="hljs-string">"Registration Failed"</span>, { <span class="hljs-attr">description</span>: <span class="hljs-string">"There was an error submitting the registration. Please try again."</span>, }); <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error submitting form:"</span>, error); }, }, queryClient); </code></pre> <p>In the above code:</p> <ul> <li><p><code>submitFormAPI</code> is the function responsible for making an API call to submit form data.</p> </li> <li><p><code>mutationFn</code> is the function executed during the mutation.</p> </li> <li><p><code>onSuccess</code> and <code>onError</code> callbacks handle the successful and failed states of the mutation.</p> </li> </ul> <p>You can use this <code>mutation</code> object in your component to trigger the form submission and handle the success or error messages via a toast notification.</p> <h3 id="heading-3-using-usequery-for-fetching-data">3. Using <code>useQuery</code> for Fetching Data</h3> <p>The <code>useQuery</code> hook is used for fetching data from the server. Let's say you want to fetch a list of todos. You can achieve that using <code>useQuery</code> as shown below:</p> <pre><code class="lang-javascript"><span class="hljs-comment">// any-component.tsx</span> <span class="hljs-keyword">import</span> { useQuery } <span class="hljs-keyword">from</span> <span class="hljs-string">"@tanstack/react-query"</span>; <span class="hljs-keyword">import</span> { queryClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'../store'</span>; <span class="hljs-keyword">const</span> getTodos = <span class="hljs-keyword">async</span> () =&gt; { <span class="hljs-comment">// Fetch todos from the API</span> <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"/api/todos"</span>); <span class="hljs-keyword">return</span> response.json(); }; <span class="hljs-keyword">const</span> query = useQuery({ <span class="hljs-attr">queryKey</span>: [<span class="hljs-string">'todos'</span>], <span class="hljs-attr">queryFn</span>: getTodos }, queryClient); </code></pre> <p>In this code:</p> <ul> <li><p><code>getTodos</code> is a function responsible for fetching the todo list from the API.</p> </li> <li><p><code>useQuery</code> fetches the data and caches it using the <code>queryClient</code> instance.</p> </li> </ul> <h3 id="heading-4-handling-tanstack-query-in-astro-react">4. Handling TanStack Query in Astro + React</h3> <p>In regular React apps, you would typically wrap your application with <code>QueryClientProvider</code> at the root level, but this approach doesn't work as expected in Astro due to its nature of partial hydration and separation of client and server logic.</p> <p>For Astro + React integration, you don’t need to wrap the <code>QueryClientProvider</code> around your app. Instead, directly import <code>queryClient</code> and pass it into the <code>useMutation</code> or <code>useQuery</code> hooks, as shown in the previous examples. This bypasses the need for a global provider and allows TanStack Query to work within the Astro ecosystem.</p> <h3 id="heading-5-complete-example">5. Complete Example</h3> <p>Here’s a simple example of how you would integrate TanStack Query in a React component within an Astro project.</p> <pre><code class="lang-javascript"><span class="hljs-comment">// src/components/TodoList.tsx</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> { useQuery } <span class="hljs-keyword">from</span> <span class="hljs-string">"@tanstack/react-query"</span>; <span class="hljs-keyword">import</span> { queryClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'../store'</span>; <span class="hljs-keyword">const</span> getTodos = <span class="hljs-keyword">async</span> () =&gt; { <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"/api/todos"</span>); <span class="hljs-keyword">return</span> response.json(); }; <span class="hljs-keyword">const</span> TodoList = <span class="hljs-function">() =&gt;</span> { <span class="hljs-keyword">const</span> { data, error, isLoading } = useQuery({ <span class="hljs-attr">queryKey</span>: [<span class="hljs-string">'todos'</span>], <span class="hljs-attr">queryFn</span>: getTodos }, queryClient); <span class="hljs-keyword">if</span> (isLoading) <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Loading...<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>; <span class="hljs-keyword">if</span> (error) <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Error loading todos<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>; <span class="hljs-keyword">return</span> ( <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span> {data.map((todo: any) =&gt; ( <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{todo.id}</span>&gt;</span>{todo.title}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span> ))} <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span></span> ); }; <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> TodoList; </code></pre> <p>This component fetches a list of todos and displays them in a list. You can include this component in your Astro pages like this:</p> <pre><code class="lang-javascript">--- <span class="hljs-keyword">import</span> TodoList <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/TodoList'</span>; --- </code></pre> <h3 id="heading-conclusion">Conclusion</h3> <p>Integrating TanStack Query into an Astro + React project might seem tricky at first, especially when the typical <code>QueryClientProvider</code> wrap method doesn't work as expected. However, as demonstrated, using <code>queryClient</code> directly within your hooks like <code>useMutation</code> and <code>useQuery</code> is a practical solution. With this setup, you can manage server-side data fetching, mutations, and caching effectively.</p> <p>Now, you can take full advantage of TanStack Query in your Astro project, ensuring optimized and well-handled server-side state management in your React components.</p> How to Solve the Expo Network Response Timed Out Problemhttps://blog.mdadul.dev/how-to-solve-the-expo-network-response-timed-out-problem-47edff53bb60/https://blog.mdadul.dev/how-to-solve-the-expo-network-response-timed-out-problem-47edff53bb60/Solve the Expo network response timed out error by opening the necessary ports on your development machine. Learn step-by-step troubleshootingThu, 08 Aug 2024 14:13:53 GMT<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771086430539/71dae1d6-d5f4-43b5-8194-9fd16d9921b7.jpeg" alt /></p> <p>If you’re developing a React Native app with Expo and you’re encountering a <strong>“network response timed out”</strong> error, it’s likely due to the port that Expo’s Metro bundler is running on not being open on your machine.</p> <p>The solution is to open the necessary port so that your device or emulator can connect to the Metro server.</p> <p><strong>Steps to Solve the Expo Network Response Timed Out Problem on Linux (Ubuntu)</strong></p> <ol> <li><strong>Check the Current Open Ports:</strong></li> </ol> <p>In your terminal, run the following command to view the currently open ports:</p> <p>sudo ufw status verbose</p> <ul> <li><p>Look for the port that Expo’s Metro bundler is running on (usually 8081).</p> </li> <li><p>If you don’t see the port listed, you’ll need to open it.</p> </li> </ul> <p>2. <strong>Open the Expo Metro Port:</strong> Run the following command to open the Expo Metro port (replace 8081 with the port number if different):</p> <p><code>sudo ufw allow 8081/tcp</code></p> <p>This will open the port and allow traffic to flow through it.</p> <p>After following these steps, your Expo “network response timed out” issue should be resolved, and you should be able to connect your device or emulator to the Expo Metro server without any problems.</p> <p>If you continue to encounter issues, make sure to check your firewall settings and ensure that the specified port is not being blocked. You may also want to try restarting your development environment or the Expo Metro server to see if that resolves the problem.</p> 20 Awesome APIs to Enhance Your Development Projectshttps://blog.mdadul.dev/20-awesome-apis-to-enhance-your-development-projects-6884bb1b41d3/https://blog.mdadul.dev/20-awesome-apis-to-enhance-your-development-projects-6884bb1b41d3/Photo by Douglas Lopes on Unsplash As a developer, having access to powerful APIs can be a game-changer, allowing you to quickly integrate robust functionality into your applications. In this post, we’ve curated a list of 20 awesome APIs spanning var...Fri, 12 Apr 2024 04:26:57 GMT<p>Photo by <a target="_blank" href="https://unsplash.com/@douglasamarelo?utm_source=medium&amp;utm_medium=referral">Douglas Lopes</a> on <a target="_blank" href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></p> <p>As a developer, having access to powerful APIs can be a game-changer, allowing you to quickly integrate robust functionality into your applications. In this post, we’ve curated a list of 20 awesome APIs spanning various domains, from image management to weather data, news, and more. Whether you’re building a web app, mobile app, or any other type of software, these APIs are sure to come in handy.</p> <p>1. Unsplash API (<a target="_blank" href="https://unsplash.com/developers">https://unsplash.com/developers</a>) — Access a massive collection of high-quality stock photos and serve them directly in your app.</p> <p>2. WatchMode API (<a target="_blank" href="https://api.watchmode.com/">https://api.watchmode.com/</a>) — Integrate data on movies, TV shows, and streaming services to build entert20 Awesome APIs to Enhance Your Development Projectsainment apps and services.</p> <p>3. Discord API (<a target="_blank" href="https://discord.com/developers/docs/reference">https://discord.com/developers/docs/reference</a>) — Build bots, automation tools, and integrations for the popular Discord chat platform.</p> <p>4. World News API (<a target="_blank" href="https://worldnewsapi.com/">https://worldnewsapi.com/</a>) — Fetch up-to-date news articles from sources around the globe for your news apps or dashboards.</p> <p>5. SVIX API (<a target="_blank" href="https://www.svix.com/">https://www.svix.com/</a>) — Access SVIX’s vector image creation API to generate unique illustrations on the fly.</p> <p>6. Kroki (<a target="_blank" href="https://kroki.io/">https://kroki.io/</a>) — Create diagrams and visualizations as code using a simple text-based syntax.</p> <p>7. Google APIs (<a target="_blank" href="https://developers.google.com/docs/api/reference/rest">https://developers.google.com/docs/api/reference/rest</a>) — Tap into Google’s vast suite of APIs for maps, cloud services, artificial intelligence, and more.</p> <p>8. HTTP Dog (<a target="_blank" href="https://http.dog/">https://http.dog/</a>) — A simple API for testing HTTP request methods like GET, POST, PUT, and more.</p> <p>9. GeoKEO (<a target="_blank" href="https://geokeo.com/">https://geokeo.com/</a>) — Geocoding and reverse geocoding APIs to map locations or extract places from coordinates.</p> <p>10. Ticketmaster API (<a target="_blank" href="https://developer.ticketmaster.com/products-and-docs/apis/getting-started/">https://developer.ticketmaster.com/products-and-docs/apis/getting-started/</a>) — Access event data and ticketing functionality for concerts, sports, and more.</p> <p>11. iLovePDF (<a target="_blank" href="https://developer.ilovepdf.com/">https://developer.ilovepdf.com/</a>) — Manipulate PDFs programmatically with APIs for compression, merging, splitting, and other operations.</p> <p>12. Ollama (<a target="_blank" href="https://ollama.com/">https://ollama.com/</a>) — Get up and running with large language models.</p> <p>13. Dictionary API (<a target="_blank" href="https://dictionaryapi.dev/">https://dictionaryapi.dev/</a>) — Look up definitions, synonyms, pronunciations, and more with a simple dictionary API.</p> <p>14. OpenWeatherMap (<a target="_blank" href="https://openweathermap.org/api">https://openweathermap.org/api</a>) — Access current weather data and forecasts for any location on Earth.</p> <p>15. Alpha Vantage (<a target="_blank" href="https://www.alphavantage.co/">https://www.alphavantage.co/</a>) — Retrieve real-time and historical stock data through this financial markets API.</p> <p>16. WordPress REST API (<a target="_blank" href="https://developer.wordpress.org/rest-api/">https://developer.wordpress.org/rest-api/</a>) — Interact with WordPress sites and content programmatically.</p> <p>17. Spotify Web API (<a target="_blank" href="https://developer.spotify.com/documentation/web-api">https://developer.spotify.com/documentation/web-api</a>) — Build applications that leverage Spotify’s music streaming service.</p> <p>18. Deepgram (<a target="_blank" href="https://deepgram.com/">https://deepgram.com/</a>) — Accurate speech-to-text transcription and recognition via API.</p> <p>19. Resend (<a target="_blank" href="https://resend.com">https://resend.com</a>) — Send emails, SMS, voice messages, push notifications and more through a single API.</p> <p>20. NASA APIs (<a target="_blank" href="https://api.nasa.gov/">https://api.nasa.gov/</a>) — Explore NASA’s open data resources like imagery, astrophysics data, Mars rover photos, and more.</p> <p>With this diverse set of APIs at your fingertips, you’ll have the building blocks to create truly innovative and feature-rich applications. Don’t hesitate to explore their documentation and get started integrating them into your next project. Happy coding!</p> Warp — The Terminal Reimagined for Modern Developershttps://blog.mdadul.dev/warp-the-terminal-reimagined-for-modern-developers-88768f221003/https://blog.mdadul.dev/warp-the-terminal-reimagined-for-modern-developers-88768f221003/[Warp-your new favorite terminalWarp is a Rust-based terminal built for speed. With Warp, you can type in a terminal like an IDE, navigate your output…app.warp.dev](https://app.warp.dev/referral/EXNJM6 "https://app.warp.dev/referral/EXNJM6") If you’r...Fri, 12 Apr 2024 04:19:08 GMT<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771086418795/2133be22-4deb-4133-abbb-ebe5fa842a07.png" alt /></p> <p>[<strong>Warp-your new favorite terminal</strong><br /><em>Warp is a Rust-based terminal built for speed. With Warp, you can type in a terminal like an IDE, navigate your output…</em>app.warp.dev](https://app.warp.dev/referral/EXNJM6 "https://app.warp.dev/referral/EXNJM6")<a target="_blank" href="https://app.warp.dev/referral/EXNJM6"></a></p> <p>If you’re a developer who spends a lot of time on the command line, you owe it to yourself to check out Warp — a groundbreaking new terminal that supercharges your productivity with AI assistance, modern editing capabilities, and collaborative workflows.</p> <p>The first thing that struck me about Warp is just how fast and responsive it feels. Built with Rust, it boots up instantly and inputs register with zero lag. But it’s the innovative feature set that really makes Warp shine.</p> <p><strong>Modern Editing:</strong> One of my biggest pain points with traditional terminals is the clunky text editing experience. Warp addresses this brilliantly with an editing mode that feels just like a modern IDE. You can use the keyboard or mouse to insert, copy, select text with ease. Vim keybindings are supported out of the box. And smart completions help you type commands faster without needing to install plugins.</p> <p><strong>AI Command Generation:</strong> This is where Warp gets really magical. You can just describe what you want to do in plain English, and Warp’s built-in AI will generate the appropriate command(s) for you. No more furiously Googling or checking man pages — Warp’s AI acts as an intelligent command line assistant. And crucially, all processing happens locally, so your data stays private.</p> <p><strong>Warp Drive Workflows:</strong> Warp Drive is a revolutionary way to save, organize and share terminal commands and workflows across your team. You can parameterize commands, categorize them into collections, and bring up any workflow on demand with a few keystrokes. For teams, Warp Drive acts as a centralized, realtime-updated knowledge base for all your company’s scripts and DevOps processes.</p> <p>I’ve barely scratched the surface of Warp’s capabilities here. It also has great customization options for themes, secure encrypted data storage, optional cloud backup, and more. After using Warp for a few weeks, going back to a traditional terminal feels like driving a beat-up pickup truck after getting used to a Tesla.</p> <p>Warp is currently in beta, but you can request access and download it for Mac, Windows or Linux at warp.dev. For any developer who wants to supercharge their command line productivity, it’s an absolute must-try. Highly recommended!</p> Introducing Queryhub: Revolutionizing Learning with AI-Powered Study Companionhttps://blog.mdadul.dev/introducing-queryhub-revolutionizing-learning-with-ai-powered-study-companion-1b9bfa39d40d/https://blog.mdadul.dev/introducing-queryhub-revolutionizing-learning-with-ai-powered-study-companion-1b9bfa39d40d/Hello friends and family, brothers and sisters, and the World!!! We’re Introducing Queryhub — Your Ultimate Study Companion! https://www.queryhub.info As students ourselves, we understand the challenges of navigating through vast amounts of academi...Sat, 06 Apr 2024 19:34:18 GMT<p>Hello friends and family, brothers and sisters, and the World!!! We’re Introducing Queryhub — Your Ultimate Study Companion! </p> <p><a target="_blank" href="https://www.queryhub.info">https://www.queryhub.info</a></p> <p>As students ourselves, we understand the challenges of navigating through vast amounts of academic information. That’s why we’ve created Queryhub, a web application designed to revolutionize the way you learn and collaborate.</p> <p><strong>Query Forum:</strong><br />Imagine a platform where students from all universities and departments can come together to ask questions, share knowledge, and find answers. With Queryhub’s AI-powered search, you’ll never struggle to find relevant information again. Can’t find what you’re looking for? Simply create a new question, and let the community provide insightful answers. Upvote, downvote, and verify solutions — it’s a true collaborative learning experience.</p> <p><strong>Query AI:</strong><br />For those who prefer diving into PDFs, books, and research papers, Queryhub’s Query AI is here to help. Simply upload your document, and our intelligent chatbot will provide you with concise answers and suggest related questions — making your reading experience more efficient and interactive.<br />We’re thrilled to have brought this project to life, and we couldn’t have done it without the hard work and dedication of our dedicated team. The pioneers are Arthi Barua, Emdadul Islam, MD. Mehrab Evan, Rafayet Habib, and Tareq Ahmed</p> <p>Join us on this exciting journey and experience the power of Queryhub! Share your thoughts and let us know how we can continue to improve your learning experience.</p> <p>© <a target="_blank" href="https://www.queryhub.info">https://www.queryhub.info</a></p> A Guide to Downloading Images from a Database using Node.jshttps://blog.mdadul.dev/a-guide-to-downloading-images-from-a-database-using-node-js-997e8ccba1c7/https://blog.mdadul.dev/a-guide-to-downloading-images-from-a-database-using-node-js-997e8ccba1c7/Learn how to download images from MongoDB using Node.js with this step-by-step tutorial. Perfect for developers using Node.js and MongoDBSat, 23 Mar 2024 23:21:46 GMT<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771086411070/a1a9cfe9-3ab5-4272-a47f-654ba2c2529e.jpeg" alt /></p> <p>Photo by <a target="_blank" href="https://unsplash.com/@sigmund?utm_source=medium&amp;utm_medium=referral">Sigmund</a> on <a target="_blank" href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></p> <p><strong>Introduction</strong>: In this tutorial, we’ll explore how to download images from a MongoDB database using Node.js. We’ll walk through setting up a Node.js project, connecting to a MongoDB database, defining a schema for image URLs, and creating a script to download and save images locally.</p> <p><strong>Prerequisites</strong>: Before we begin, ensure you have the following:</p> <ol> <li><p>Basic knowledge of JavaScript and Node.js.</p> </li> <li><p>Node.js installed on your machine.</p> </li> <li><p>Access to a MongoDB database with a collection containing image URLs.</p> </li> </ol> <p><strong>Step 1: Set Up Your Project:</strong> Let’s start by setting up our Node.js project. Open your terminal and execute the following commands:</p> <pre><code class="lang-bash">mkdir image-downloader <span class="hljs-built_in">cd</span> image-downloader npm init -y </code></pre> <p>Next, install the required dependencies:</p> <p><code>npm install mongoose</code></p> <p><strong>Step 2: Connect to MongoDB:</strong> Create a file named <code>dbConnect.js</code> inside a <code>config</code> directory to handle the MongoDB connection:</p> <pre><code class="lang-javascript"><span class="hljs-comment">// config/dbConnect.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> connectDB = <span class="hljs-keyword">async</span> () =&gt; { <span class="hljs-keyword">try</span> { <span class="hljs-keyword">await</span> mongoose.connect(<span class="hljs-string">'mongodb://localhost:27017/your-database-name'</span>, { <span class="hljs-attr">useNewUrlParser</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">useUnifiedTopology</span>: <span class="hljs-literal">true</span>, }); <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'MongoDB Connected'</span>); } <span class="hljs-keyword">catch</span> (error) { <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error connecting to MongoDB:'</span>, error); process.exit(<span class="hljs-number">1</span>); } }; <span class="hljs-built_in">module</span>.exports = connectDB; </code></pre> <p>Make sure to replace <code>'mongodb://localhost:27017/your-database-name'</code> with your actual MongoDB connection URL.</p> <p>Step 3: Define the Member Model: Create a file named <code>member.js</code> inside a <code>model</code> directory to define the schema for your <code>Member</code> model:</p> <pre><code class="lang-javascript"><span class="hljs-comment">// model/member.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> memberSchema = <span class="hljs-keyword">new</span> mongoose.Schema({ <span class="hljs-attr">image</span>: <span class="hljs-built_in">String</span>, }); <span class="hljs-keyword">const</span> Member = mongoose.model(<span class="hljs-string">'Member'</span>, memberSchema); <span class="hljs-built_in">module</span>.exports = Member; </code></pre> <p><strong>Step 4: Download Images:</strong> Now, let’s create a script named <code>downloadImages.js</code> in the root directory of our project:</p> <pre><code class="lang-javascript"><span class="hljs-comment">// downloadImages.js</span> <span class="hljs-keyword">const</span> Member = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./model/member'</span>); <span class="hljs-keyword">const</span> initDB = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./config/dbConnect'</span>); <span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>); <span class="hljs-keyword">const</span> https = <span class="hljs-built_in">require</span>(<span class="hljs-string">'https'</span>); initDB(); <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">downloadImage</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-keyword">try</span> { <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'DB Member fetching images...'</span>); <span class="hljs-keyword">const</span> members = <span class="hljs-keyword">await</span> Member.find({}); <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'DB Member fetched'</span>); <span class="hljs-built_in">console</span>.log(members); <span class="hljs-keyword">const</span> ImageUrls = members.map(<span class="hljs-function">(<span class="hljs-params">member</span>) =&gt;</span> member.image); <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Downloading images...'</span>); <span class="hljs-keyword">await</span> <span class="hljs-built_in">Promise</span>.all( ImageUrls.map(<span class="hljs-keyword">async</span> (url, index) =&gt; { <span class="hljs-keyword">const</span> imageName = url.split(<span class="hljs-string">'/'</span>).pop(); <span class="hljs-keyword">const</span> file = fs.createWriteStream(<span class="hljs-string">`public/members/<span class="hljs-subst">${imageName}</span>`</span>); https .get(url, <span class="hljs-function">(<span class="hljs-params">response</span>) =&gt;</span> { response.pipe(file); file.on(<span class="hljs-string">'finish'</span>, <span class="hljs-function">() =&gt;</span> { file.close(); <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Image <span class="hljs-subst">${index}</span> downloaded as <span class="hljs-subst">${imageName}</span>`</span>); }); }) .on(<span class="hljs-string">'error'</span>, <span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> { fs.unlink(<span class="hljs-string">`public/members/<span class="hljs-subst">${imageName}</span>`</span>); <span class="hljs-built_in">console</span>.error(<span class="hljs-string">`Error downloading image: <span class="hljs-subst">${err.message}</span>`</span>); }); }) ); } <span class="hljs-keyword">catch</span> (error) { <span class="hljs-built_in">console</span>.error(error); process.exit(<span class="hljs-number">1</span>); } } downloadImage(); </code></pre> <p><strong>Step 5: Run the Script:</strong> Execute the following command in your terminal to start downloading the images:</p> <p><code>node downloadImages.js</code></p> <p>This script fetches image URLs from the MongoDB database, downloads each image using HTTPS, and saves them in the <code>public/members</code> directory.</p> <p>Conclusion: Congratulations! You’ve successfully created a script to download images from a MongoDB database using Node.js. Feel free to integrate this script into your Node.js applications to handle image downloads efficiently.</p> Setting Up Seeders in Node.js and Mongoose with Examplehttps://blog.mdadul.dev/setting-up-seeders-in-node-js-and-mongoose-with-example-828da1bf89f1/https://blog.mdadul.dev/setting-up-seeders-in-node-js-and-mongoose-with-example-828da1bf89f1/Learn to set up seeders in Node.js with Mongoose to populate your MongoDB database, focusing on creating an admin user during seedingWed, 10 Jan 2024 11:06:41 GMT<p>Photo by <a target="_blank" href="https://unsplash.com/@6heinz3r?utm_source=medium&amp;utm_medium=referral">Gabriel Heinzer</a> on <a target="_blank" href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></p> <p><strong>Introduction:</strong> In Node.js applications using MongoDB with Mongoose, seeders play a crucial role in populating the database with initial data. This article will guide you through the process of setting up seeders, with a specific focus on creating an admin user during the seeding process.</p> <p><strong>Understanding Seeders:</strong> Seeders are scripts designed to populate your database with predefined data. They are essential for maintaining a consistent and realistic database state during development and testing.</p> <p><strong>Installing Necessary Packages:</strong> Ensure you have the required packages installed for your Node.js and Mongoose projects. If not, install Mongoose:</p> <p>npm install mongoose</p> <p><strong>Project Structure:</strong> Organize your project with designated folders for models and seeders. Here’s an example structure:</p> <pre><code class="lang-javascript">project-root ├── models │ └── user.js ├── seeders │ └── admin-seeder.js └── app.js </code></pre> <p><strong>Creating a Mongoose Model</strong>: Define a Mongoose model for the data you want to seed. For this example, let’s create a simple User model.</p> <pre><code class="lang-javascript"><span class="hljs-comment">// models/user.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> userSchema = <span class="hljs-keyword">new</span> mongoose.Schema({ <span class="hljs-attr">name</span>: <span class="hljs-built_in">String</span>, <span class="hljs-attr">password</span>: <span class="hljs-built_in">String</span>, <span class="hljs-attr">isAdmin</span>: <span class="hljs-built_in">Boolean</span>, <span class="hljs-comment">// Add other fields as needed</span> }); <span class="hljs-keyword">const</span> UserModel = mongoose.model(<span class="hljs-string">"User"</span>, userSchema); <span class="hljs-built_in">module</span>.exports = UserModel; </code></pre> <p><strong>Writing the Admin Seeder Script:</strong> Create the seeder script to populate the database, including the creation of an admin user.</p> <pre><code class="lang-javascript"><span class="hljs-comment">// seeders/admin-seeder.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> bcrypt = <span class="hljs-built_in">require</span>(<span class="hljs-string">"bcryptjs"</span>); <span class="hljs-keyword">const</span> UserModel = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../models/user"</span>); <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">seedAdmin</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-comment">// Connect to the database</span> <span class="hljs-keyword">await</span> mongoose.connect(<span class="hljs-string">"mongodb://localhost/your-database"</span>, { <span class="hljs-attr">useNewUrlParser</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">useUnifiedTopology</span>: <span class="hljs-literal">true</span>, }); <span class="hljs-comment">// Check if admin already exists</span> <span class="hljs-keyword">const</span> existingAdmin = <span class="hljs-keyword">await</span> UserModel.findOne({ <span class="hljs-attr">isAdmin</span>: <span class="hljs-literal">true</span> }); <span class="hljs-keyword">if</span> (!existingAdmin) { <span class="hljs-comment">// Create admin credentials</span> <span class="hljs-keyword">const</span> adminCredentials = { <span class="hljs-attr">name</span>: <span class="hljs-string">"Admin User"</span>, <span class="hljs-attr">password</span>: <span class="hljs-string">"adminpassword"</span>, <span class="hljs-attr">isAdmin</span>: <span class="hljs-literal">true</span>, <span class="hljs-comment">// Add other fields as needed</span> }; <span class="hljs-comment">// Hash the admin password</span> <span class="hljs-keyword">const</span> salt = <span class="hljs-keyword">await</span> bcrypt.genSalt(<span class="hljs-number">10</span>); <span class="hljs-keyword">const</span> hashedPassword = <span class="hljs-keyword">await</span> bcrypt.hash(adminCredentials.password, salt); adminCredentials.password = hashedPassword; <span class="hljs-comment">// Create admin user</span> <span class="hljs-keyword">await</span> UserModel.create(adminCredentials); <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Admin user created successfully"</span>); } <span class="hljs-keyword">else</span> { <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Admin user already exists"</span>); } <span class="hljs-comment">// Close the database connection</span> <span class="hljs-keyword">await</span> mongoose.disconnect(); } <span class="hljs-comment">// Execute the admin seeder</span> seedAdmin().then(<span class="hljs-function">() =&gt;</span> { <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Admin seeding completed"</span>); process.exit(<span class="hljs-number">0</span>); }).catch(<span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> { <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error seeding admin:"</span>, err); process.exit(<span class="hljs-number">1</span>); }); </code></pre> <p><strong>Running the Admin Seeder Script</strong>: Execute the admin seeder script whenever you need to populate the database, including the creation of the admin user.</p> <p><code>node seeders/admin-seeder.js</code></p> <p><strong>Conclusion</strong>: Setting up seeders in Node.js and Mongoose provides an efficient way to populate your database with initial data. By incorporating an example of creating an admin user during the seeding process, this guide helps you establish a solid foundation for managing your database seeders in Node.js projects.</p> Resolving “Window is not defined” Error during npm run build in Next.js SSR Applicationhttps://blog.mdadul.dev/resolving-window-is-not-defined-error-during-npm-run-build-in-next-js-ssr-application-67e2c6197425/https://blog.mdadul.dev/resolving-window-is-not-defined-error-during-npm-run-build-in-next-js-ssr-application-67e2c6197425/Learn how to fix the "window is not defined" error in Next.js SSR applications with conditional rendering and dynamic importsThu, 07 Dec 2023 11:36:19 GMT<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771086438433/0b8d57ad-89cc-4e56-9e08-0f2d93ed515a.png" alt /></p> <p>Window is not defined solution</p> <h3 id="heading-background">Background</h3> <p>Our development team encountered a critical issue while running the <code>npm run build</code> command in a Next.js server-side rendered (SSR) application. The error message indicated that <strong>"window is not defined,"</strong> pointing to a problem related to server-side rendering.</p> <h3 id="heading-problem-statement">Problem Statement</h3> <p>When attempting to build the project using the npm script for production (<code>npm run build</code>), the build process failed with an error that specifically mentioned the absence of the "<strong>window</strong>" object. This issue is common in Next.js applications that utilize server-side rendering, as certain components may attempt to access the "<strong>window</strong>" object, which is not available on the server side.</p> <h3 id="heading-investigation">Investigation</h3> <p>To identify the root cause of the problem, our development team thoroughly examined the components and dependencies involved in the build process. It was determined that certain components were trying to access the “<strong>window</strong>” object directly, causing the build to fail.</p> <h3 id="heading-solution">Solution</h3> <p>To resolve the “<strong>window is not defined</strong>” error during the build process, we implemented the following solution:</p> <h4 id="heading-1-conditional-rendering">1. Conditional Rendering</h4> <p>We modified the affected components to conditionally check whether the “<strong>window</strong>” object is defined before attempting to access it. This conditional rendering ensures that the problematic code is only executed on the client side, preventing errors during server-side rendering.</p> <pre><code class="lang-javascript"><span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> <span class="hljs-built_in">window</span> !== <span class="hljs-string">"undefined"</span>) { <span class="hljs-comment">// Code that relies on the window object</span> <span class="hljs-comment">// ...</span> } </code></pre> <p>This simple check ensures that the code within the conditional block is only executed in a client-side context where the “window” object is available.</p> <h4 id="heading-2-dynamic-import-with-nextjs">2. Dynamic Import with Next.js</h4> <p>To handle the SSR-related issues more gracefully, we leveraged Next.js dynamic imports with the <code>ssr: false</code> option. By doing this, we allowed specific components to be loaded dynamically on the client side, avoiding potential conflicts during server-side rendering.</p> <pre><code class="lang-javascript"><span class="hljs-keyword">import</span> dynamic <span class="hljs-keyword">from</span> <span class="hljs-string">'next/dynamic'</span>; <span class="hljs-keyword">const</span> DynamicComponent = dynamic(<span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">'../path/to/component'</span>), { <span class="hljs-attr">ssr</span>: <span class="hljs-literal">false</span>, }); </code></pre> <p>This approach ensures that the designated component is only loaded on the client side, mitigating any issues related to the “window is not defined” error during the build process.</p> <h3 id="heading-results">Results</h3> <p>Implementing the above solutions successfully resolved the “window is not defined” error during the <code>npm run build</code> command in our Next.js SSR application. The build process now completes without any issues, and the application functions as expected in both development and production environments.</p> <p>This case study highlights the importance of understanding the nuances of server-side rendering in Next.js applications and provides a practical solution for mitigating common challenges associated with the “window is not defined” error during the build process.</p> Integrating OAuth with Google using the Devise Gem(ROR): Comprehensive Documentationhttps://blog.mdadul.dev/integrating-oauth-with-google-using-the-devise-gem-ror-comprehensive-documentation-148fb71f23a6/https://blog.mdadul.dev/integrating-oauth-with-google-using-the-devise-gem-ror-comprehensive-documentation-148fb71f23a6/Photo by Mitchell Luo on Unsplash Introduction OAuth (Open Authorization) is an industry-standard protocol for authorization, enabling third-party applications to access user data without requiring the user to share their login credentials. In this d...Fri, 24 Nov 2023 16:43:05 GMT<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771086473295/96092cd0-1aa5-458b-8144-23a0d30b1ccb.jpeg" alt /></p> <p>Photo by <a target="_blank" href="https://unsplash.com/@mitchel3uo?utm_source=medium&amp;utm_medium=referral">Mitchell Luo</a> on <a target="_blank" href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></p> <h3 id="heading-introduction">Introduction</h3> <p>OAuth (Open Authorization) is an industry-standard protocol for authorization, enabling third-party applications to access user data without requiring the user to share their login credentials. In this documentation, we will guide you through the process of implementing OAuth using the <code>google_oauth2</code> gem in a Ruby on Rails application with Devise for authentication.</p> <h3 id="heading-prerequisites">Prerequisites</h3> <p>Before you begin, ensure that you have the following:</p> <ul> <li>Ruby installed on your machine (version 2.5 or higher)</li> <li>Ruby on Rails framework installed (version 5.0 or higher)</li> <li>An existing Ruby on Rails application</li> <li>Basic knowledge of Ruby on Rails and Devise gem</li> </ul> <h3 id="heading-step-1-setup-google-developer-console">Step 1: Setup Google Developer Console</h3> <p>Before integrating OAuth with Google, you need to set up a project in the Google Developer Console and obtain credentials. Follow these steps:</p> <ol> <li>Visit the <a target="_blank" href="https://console.developers.google.com/">Google Developer Console</a> and create a new project.</li> <li>Enable the <strong>Google+ API</strong> by navigating to the <strong>Library</strong> section and searching for “Google+ API.” Click on it and enable it for your project.</li> <li>Navigate to the <strong>Credentials</strong> section and click on the <strong>Create Credentials</strong> button. Choose <strong>OAuth client ID</strong>.</li> <li>Configure the OAuth client ID settings. Select <strong>Web Application</strong> as the application type.</li> <li>In the <strong>Authorized Redirect URIs</strong> field, enter the callback URL where Google will redirect the user after authentication. The format of the callback URL will be <code>http://localhost:3000/users/auth/google_oauth2/callback</code> (replace <code>localhost:3000</code> with your actual domain and port).</li> <li>Save the OAuth client ID and secret generated by Google. You will need these later in your Rails application.</li> </ol> <h3 id="heading-step-2-add-googleoauth2-gem-to-your-gemfile">Step 2: Add ‘google_oauth2’ Gem to Your Gemfile</h3> <p>To integrate OAuth with Google in your Rails application, you need to add the <code>google_oauth2</code> gem to your Gemfile and install it using Bundler. Follow these steps:</p> <ol> <li>Open your application’s Gemfile using a text editor.</li> <li>Add the following line to the Gemfile:</li> </ol> <p>gem 'omniauth-google-oauth2'</p> <p>3. Save the file and close it.</p> <p>4.Open your terminal or command prompt and navigate to your application’s directory.</p> <p>5. Run the following command to install the gem:</p> <p>$ bundle install</p> <h3 id="heading-step-3-configure-devise-for-oauth">Step 3: Configure Devise for OAuth</h3> <p>Next, you need to configure Devise to work with OAuth using the <code>google_oauth2</code> gem. Follow these steps:</p> <ol> <li>Open the file <code>config/initializers/devise.rb</code>.</li> <li>Locate the <code>config.omniauth</code> block and uncomment it (remove the leading '#' character).</li> <li>Modify the <code>config.omniauth</code> block to include the following lines:</li> </ol> <p>config.omniauth :google_oauth2, 'GOOGLE_CLIENT_ID', 'GOOGLE_CLIENT_SECRET', { access_type: 'offline', prompt: 'consent' }</p> <p>Replace <code>'GOOGLE_CLIENT_ID'</code> and <code>'GOOGLE_CLIENT_SECRET'</code> with the credentials obtained from the Google Developer Console.</p> <p>4. Save the file.</p> <h3 id="heading-step-5-implement">Step 5: Implement</h3> <p>the OmniauthCallbacksController Now, you need to create a controller to handle the OAuth callback. Follow these steps:</p> <ol> <li>Create a new file called <code>app/controllers/users/omniauth_callbacks_controller.rb</code>.</li> <li>Define the <code>OmniauthCallbacksController</code> class and inherit from <code>Devise::OmniauthCallbacksController</code>. Implement the callback method as follows:</li> </ol> <p>class Users::OmniauthCallbacksController &lt; Devise::OmniauthCallbacksController<br /> def google_oauth2<br /> @user = User.from_omniauth(request.env['omniauth.auth'])<br /> if @user.persisted?<br /> sign_in_and_redirect @user, event: :authentication<br /> set_flash_message(:notice, :success, kind: 'Google') if is_navigational_format?<br /> else<br /> redirect_to new_user_registration_url<br /> end<br /> end<br />end</p> <p>In this example, the <code>User.from_omniauth</code> method is responsible for finding or creating a user based on the OAuth response.</p> <p>3. Save the file.</p> <h3 id="heading-step-6-implement-the-user-model-method">Step 6: Implement the User Model Method</h3> <p>To handle the user creation or authentication based on the OAuth response, you need to implement a method in your User model. Follow these steps:</p> <ol> <li>Open the file <code>app/models/user.rb</code>.</li> <li>Add the following code to define the <code>from_omniauth</code> method:</li> </ol> <p>class User &lt; ApplicationRecord<br /> # ... </p> <p> def self.from_omniauth(auth)<br /> where(provider: auth.provider, uid: auth.uid).first_or_create do |user|<br /> user.email = auth.info.email<br /> user.password = Devise.friendly_token[0, 20]<br /> # Additional user attributes can be set here based on the auth response<br /> end<br /> end </p> <p> # ...<br />end</p> <p>This example creates a new user with the provided email and generates a random password if a user with the same provider and UID combination does not already exist.</p> <p>3. Save the file.</p> <h3 id="heading-step-7-update-user-views">Step 7: Update User Views</h3> <p>Finally, you need to update your user views to include a link or button to trigger the OAuth authentication process. Follow these steps:</p> <ol> <li>Open the view file where you want to place the OAuth authentication link/button (e.g., <code>app/views/devise/sessions/new.html.erb</code>).</li> <li>Add the following code to include the link/button:</li> </ol> <p>&lt;%= link_to "Sign in with Google", user_google_oauth2_omniauth_authorize_path %&gt;</p> <p>You can style this link/button according to your application’s design.</p> <p>3. Save the file.</p> <h3 id="heading-conclusion">Conclusion</h3> <p>Congratulations! You have successfully implemented OAuth with the Google Devise gem in your Ruby on Rails application. Users can now authenticate with their Google accounts using OAuth. You can further customize the user model and views to suit your application’s needs. Refer to the Devise and <code>omniauth-google-oauth2</code> documentation for more advanced usage and customization options.</p> Mastering GitHub OmniAuth Integration in Ruby on Rails: A Step-by-Step Guidehttps://blog.mdadul.dev/to-set-up-omniauth-with-github-in-a-ruby-on-rails-application-follow-these-steps-876c20c2094c/https://blog.mdadul.dev/to-set-up-omniauth-with-github-in-a-ruby-on-rails-application-follow-these-steps-876c20c2094c/Photo by Roman Synkevych on Unsplash Step 1: Go to the GitHub Developer Settings page: https://github.com/settings/developers Click on the “New OAuth App” button. Fill in the required fields: Application Name: Choose a name for your application. H...Fri, 24 Nov 2023 16:23:49 GMT<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771086442652/00ba92f1-8204-4409-8401-534a17d07986.jpeg" alt /></p> <p>Photo by <a target="_blank" href="https://unsplash.com/@synkevych?utm_source=medium&amp;utm_medium=referral">Roman Synkevych</a> on <a target="_blank" href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></p> <h3 id="heading-step-1">Step 1:</h3> <ol> <li>Go to the GitHub Developer Settings page: <a target="_blank" href="https://github.com/settings/developers**">https://github.com/settings/developers</a></li> <li>Click on the “New OAuth App” button.</li> <li><p>Fill in the required fields:</p> </li> <li><p>Application Name: Choose a name for your application.</p> </li> <li>Homepage URL: This should be the URL of your application’s home page.</li> <li>Authorization callback URL: This should be the URL where GitHub will redirect users after authentication. For development, use something like <code>[http://localhost:3000/auth/github/callback](http://localhost:3000/auth/github/callback.)</code><a target="_blank" href="http://localhost:3000/auth/github/callback.">.</a></li> </ol> <p>4. Click on “Register application” to create the OAuth application.</p> <p>5. Once created, take note of the “Client ID” and “Client Secret” values, as you’ll need them later.</p> <h4 id="heading-step-2-add-the-omniauth-github-gem">Step 2: Add the omniauth-github gem</h4> <p>In your Rails application’s Gemfile, add the following line:</p> <p>gem 'omniauth-github’<br />gem "omniauth-rails_csrf_protection" </p> <p>Then run <code>bundle install</code> in your terminal.</p> <h3 id="heading-step-3-configure-omniauth">Step 3: Configure OmniAuth</h3> <p>First define your application id and secret in <code>config/initializers/devise.rb</code>. 273 no line. Configuration options can be passed as the last parameter here as key/value pairs.</p> <p>config.omniauth :guthub, 'github_CLIENT_ID', 'github_CLIENT_SECRET', {}</p> <p>Then add the following to ‘<code>config/routes.rb</code>’so the callback routes are defined.</p> <p>devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }</p> <p>Make sure your model is omniauthable. Generally this is <code>/app/models/user.rb</code></p> <p>devise :omniauthable, omniauth_providers: [:github]</p> <p>Then make sure your callbacks controller is setup <code>app/controllers/users/omniauth_callbacks_controller.rb:</code></p> <p># app/controllers/users/omniauth_callbacks_controller.rb: </p> <p>class Users::OmniauthCallbacksController &lt; Devise::OmniauthCallbacksController<br /> def github<br /> # You need to implement the method below in your model (e.g. app/models/user.rb)<br /> @user = User.from_omniauth(request.env['omniauth.auth']) </p> <p> if @user.persisted?<br /> flash[:notice] = I18n.t 'devise.omniauth_callbacks.success', kind: 'Github'<br /> sign_in_and_redirect @user, event: :authentication<br /> else<br /> session['devise.github_data'] = request.env['omniauth.auth'].except('extra') # Removing extra as it can overflow some session stores<br /> redirect_to new_user_registration_url, alert: @user.errors.full_messages.join("\n")<br /> end<br /> end<br />end</p> <p>and bind to or create the user <code>model/user.rb</code></p> <p>def self.from_omniauth(access_token)<br /> data = access_token.info<br /> user = User.where(email: data['email']).first </p> <p> # Uncomment the section below if you want users to be created if they don't exist<br /> unless user<br /> user = User.create(<br /> email: data['email'],<br /> password: Devise.friendly_token[0,20]<br /> )<br /> end<br /> user<br />end</p> <h4 id="heading-thats-it-your-app-now-supports-github-authentication">That’s it! Your app now supports GitHub authentication.</h4> <p>If your face any error, like can not verify CSRF token, then add this command in <code>controller/application_controller.rb</code></p> <p>skip_before_action :verify_authenticity_token</p> A Guide to Web Accessibilityhttps://blog.mdadul.dev/crafting-digital-spaces-for-everyone-your-guide-to-web-accessibility-5dcd38eae6ac/https://blog.mdadul.dev/crafting-digital-spaces-for-everyone-your-guide-to-web-accessibility-5dcd38eae6ac/Hey there, fellow creators! Today, let’s dive into something that’s close to our hearts—web accessibility. Imagine a world where everyone can effortlessly surf the digital realm, regardless of their abilities. In this blog post, we’re talking about m...Thu, 16 Nov 2023 23:52:43 GMT<p>Hey there, fellow creators! Today, let’s dive into something that’s close to our hearts—web accessibility. Imagine a world where everyone can effortlessly surf the digital realm, regardless of their abilities. In this blog post, we’re talking about making the web a welcoming place for every user.</p> <p>Let’s kick things off with a simple truth: web accessibility matters. It’s not about ticking off boxes; it’s about creating a space where a diverse range of users can feel at home. Think about someone using a screen reader or navigating with a keyboard—web accessibility ensures their journey is just as smooth and enjoyable.</p> <p>Now, let’s break down the essentials — the Web Content Accessibility Guidelines (WCAG). Picture these guidelines as a friendly map guiding us toward inclusivity. They cover everything from making content adaptable to providing alternatives for images, forming the backbone of an online world that’s open to all.</p> <p>Enough theory — let’s get hands-on. Imagine coding with semantic HTML and effortlessly adding alternative text to your images. These aren’t just best practices; they’re small steps that collectively make a big difference. We’ll explore practical tips to make web accessibility an integral part of our creative process.</p> <p>Think of design not just as colors and layouts, but as a philosophy. Inclusive design is like setting a place at the table for everyone from the get-go. It ensures that our websites aren’t just functional but are warm, welcoming spaces for every visitor, regardless of their abilities.</p> <p>Imagine having a toolkit that helps you ensure your website is accessible to everyone. Visualize tools like screen reader simulators and color contrast checkers — your allies in crafting a digital space that’s not just functional but beautifully accessible.</p> <p>Let’s bring it back to people. Imagine talking to users with different needs and experiences. The power of empathy in our work becomes evident as we consider their insights. It’s about making choices that resonate universally, creating interfaces that genuinely understand and cater to the diversity of human experiences.</p> <p>Now, let’s zoom out. Picture a shift happening in our industry. It’s not just about following rules; it’s a movement. Imagine being part of a community where standards evolve, regulations make the web more inclusive, and advocacy becomes the driving force. It’s about being part of something bigger than ourselves.</p> <p>As we wrap up, imagine the impact of our collective efforts. Every line of code, every design decision — we’re shaping a web where accessibility is the norm, not the exception. Let’s keep this journey going, encouraging each other to champion inclusivity in every aspect of our work. The future of web development? It’s inclusive, and it’s in our hands. Ready to make a difference in the world of accessible web development? Let’s do this together!</p> Boost Your Website’s Speed Using Contemporary Picture Formatshttps://blog.mdadul.dev/unleashing-speed-elevating-web-performance-with-next-gen-image-formats-fe180e81fe2f/https://blog.mdadul.dev/unleashing-speed-elevating-web-performance-with-next-gen-image-formats-fe180e81fe2f/Welcome to the fast-paced universe of web development, where optimizing website performance reigns supreme. Today, let’s uncover the often-overlooked magic of image formats and their profound impact on web performance and loading times. Optimized ima...Thu, 16 Nov 2023 23:51:01 GMT<p>Welcome to the fast-paced universe of web development, where optimizing website performance reigns supreme. Today, let’s uncover the often-overlooked magic of image formats and their profound impact on web performance and loading times.</p> <p>Optimized images are the unsung heroes of web performance. They not only enhance the user experience but also play a crucial role in shaping page loading times. Imagine a website that loads swiftly and delights users with seamless visuals—that's the power of image optimization we’re about to explore.</p> <p>Meet the superheroes of image formats—WebP and AVIF. WebP boasts lossless and lossy compression, while AVIF takes it a step further with even smaller file sizes. Take, for instance, a high-resolution image that typically weighs down a page. Converting it to WebP or AVIF can significantly reduce its size without compromising quality, ensuring a faster, more responsive website.</p> <p>Now, let’s get our hands dirty. Implementing WebP and AVIF in your web development toolkit is easier than you might think. Imagine a snippet of code seamlessly integrated into your project, transforming images on the fly. Tools like <strong>ImageMagick</strong> or online converters make the conversion process a breeze, making it accessible for developers of all levels.</p> <p>Time for a showdown! Imagine a website with traditional formats like JPEG and PNG. Now, let’s compare its loading speed with the same website using WebP and AVIF. The data speaks volumes — faster loading times mean happier users. Metrics don’t lie, and the impact on user experience is clear when you make the switch to next-gen image formats.</p> <p>Not every browser speaks the language of WebP and AVIF fluently. Here’s where the magic happens. Imagine a strategy that ensures a seamless experience for users, regardless of their browser. While modern browsers embrace these formats, implementing graceful fallbacks ensures that users on older browsers don’t miss out on the optimized experience.</p> <p>Time for success stories! Imagine a website that faced sluggish loading times until it embraced WebP and AVIF. Imagine the transformation—a faster, more efficient website that keeps users engaged. These aren’t just tales; they’re real-world examples paving the way for your success in the realm of optimized web performance.</p> <p>Bravo! Let’s wrap up this performance-packed journey. Imagine the impact of adopting next-gen image formats—it's not just a trend but a necessity for turbocharging your web projects. Bid farewell to sluggish loading times and usher in a faster, more efficient user experience. The speed revolution begins with a simple choice: embracing WebP and AVIF for a web that’s both swift and stunning. Are you ready for the transformation?</p> Exploring the Most Used Linux Commands with Exampleshttps://blog.mdadul.dev/exploring-the-most-used-linux-commands-with-examples-dc347a7d34b6/https://blog.mdadul.dev/exploring-the-most-used-linux-commands-with-examples-dc347a7d34b6/Photo by Gabriel Heinzer on Unsplash Introduction: Linux, with its powerful command-line interface (CLI), is a favorite among developers, system administrators, and enthusiasts alike. It offers a plethora of commands that make it a versatile and effi...Mon, 09 Oct 2023 17:25:30 GMT<p>Photo by <a target="_blank" href="https://unsplash.com/@6heinz3r?utm_source=medium&amp;utm_medium=referral">Gabriel Heinzer</a> on <a target="_blank" href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></p> <p><strong>Introduction:</strong></p> <p>Linux, with its powerful command-line interface (CLI), is a favorite among developers, system administrators, and enthusiasts alike. It offers a plethora of commands that make it a versatile and efficient operating system. In this article, we’ll dive into some of the most commonly used Linux commands, providing examples to help you understand their functionality and usefulness.</p> <p><strong>1. ls — List Directory Contents</strong></p> <p>The <code>ls</code> command is the go-to tool for listing the contents of a directory. By default, it displays files and directories in the current directory.</p> <p>Example:</p> <p>$ ls</p> <p><strong>2. cd — Change Directory</strong></p> <p><code>cd</code> is used to change the current working directory. It allows you to navigate through the file system.</p> <p>Example:</p> <p>$ cd /path/to/directory </p> <p><strong>3. pwd — Print Working Directory</strong></p> <p>To know your current location in the file system, use the `pwd` command.</p> <p>Example:</p> <p>$ pwd</p> <p><strong>4. mkdir — Create Directory</strong></p> <p>Need to create a new directory? Use <code>mkdir</code>.</p> <p>Example:</p> <p>$ mkdir new_directory </p> <p><strong>5. rm — Remove Files and Directories</strong></p> <p><code>rm</code> is used for deleting files and directories. Be cautious with this command, as it can lead to irreversible data loss.</p> <p>Example:</p> <p>$ rm file.txt<br />$ rm -r directory/</p> <p><strong>6. cp — Copy Files and Directories</strong></p> <p>Use <code>cp</code> to copy files and directories.</p> <p>Example:</p> <p>$ cp file.txt new_file.txt<br />$ cp -r directory/ new_directory/</p> <p><strong>7. mv — Move or Rename Files and Directories</strong></p> <p><code>mv</code> is used for moving files and directories. It can also rename files and directories.</p> <p>Example:</p> <p>$ mv old_file.txt new_location/<br />$ mv old_name.txt new_name.txt</p> <p><strong>8. touch — Create Empty Files</strong></p> <p>Create an empty file using the <code>touch</code> command.</p> <p>Example:</p> <p>$ touch new_file.txt</p> <p><strong>9. cat — Concatenate and Display File Contents</strong></p> <p><code>cat</code> displays the contents of a file on the terminal.</p> <p>Example:</p> <p>$ cat file.txt</p> <p><strong>10. grep — Search Text Using Patterns</strong></p> <p><code>grep</code> is a powerful tool for searching text within files using patterns.</p> <p>Example:</p> <p>$ grep “search_term” file.txt</p> <p><strong>11.ps — Display Information about Running Processes</strong></p> <p>`ps` shows information about the current running processes.</p> <p>Example:</p> <p>$ ps aux</p> <p><strong>12. kill — Terminate Processes</strong></p> <p>Use <code>kill</code> to terminate processes by their process ID (PID).</p> <p>Example:</p> <p>$ kill -9 PID</p> <p>13. df — Display Disk Space Usage</p> <p><code>df</code> provides information about disk space usage on your system.</p> <p>Example:</p> <p>$ df -h</p> <p><strong>14. du — Display Directory/File Space Usage</strong></p> <p><code>du</code> displays the space usage of directories and files.</p> <p>Example:</p> <p>$ du -sh directory/</p> <p><strong>15. chmod — Change File Permissions</strong></p> <p><code>chmod</code> is used to change file permissions.</p> <p>Example:</p> <p>$ chmod 755 file.sh</p> <p>Conclusion</p> <p>These are just a few of the most commonly used Linux commands. Familiarizing yourself with these commands and their usage will greatly enhance your ability to work efficiently on a Linux system. As you become more proficient with the Linux CLI, you’ll discover countless other commands that can help you perform various tasks and manage your system effectively. So, keep exploring and mastering the Linux command-line interface to become a proficient Linux user.</p> How to Set Up Husky, ESLint, and Prettier in a Node.js Projecthttps://blog.mdadul.dev/setting-up-husky-eslint-and-prettier-in-a-node-js-application-f1623ae3deab/https://blog.mdadul.dev/setting-up-husky-eslint-and-prettier-in-a-node-js-application-f1623ae3deab/Photo by Pakata Goh on Unsplash In this tutorial, we will walk you through the process of setting up Husky, ESLint, and Prettier in a Node.js application. These tools will help you maintain consistent code quality, enforce coding standards, and ensur...Fri, 11 Aug 2023 17:57:08 GMT<p>Photo by <a target="_blank" href="https://unsplash.com/@pakata?utm_source=medium&amp;utm_medium=referral">Pakata Goh</a> on <a target="_blank" href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></p> <p>In this tutorial, we will walk you through the process of setting up Husky, ESLint, and Prettier in a Node.js application. These tools will help you maintain consistent code quality, enforce coding standards, and ensure that your codebase remains clean and error-free. We’ll cover everything from installation to advanced configuration.</p> <p><strong>Prerequisites</strong></p> <p>Before we begin, make sure you have the following installed on your system:</p> <p>- <code>Node.js</code> (with <code>npm</code>, the <code>Node.js</code> package manager) — You can download it from the official <code>Node.js</code> website: <a target="_blank" href="https://nodejs.org/">https://nodejs.org/</a></p> <p><strong>Step 1: Create a Node.js Project</strong></p> <p>If you don’t already have a <code>Node.js</code> project, you can create one using the following commands:</p> <p>mkdir my-node-app<br />cd my-node-app<br />npm init -y</p> <p><strong>Step 2: Install Dependencies</strong></p> <p>In this step, we’ll install the necessary dependencies: <code>ESLint</code>, <code>Prettier</code>, and <code>Husky</code>.</p> <p>npm install eslint prettier husky — save-dev</p> <p><strong>Step 3: Configure ESLint</strong></p> <p>Create an <code>ESLint</code> configuration file in your project’s root directory. You can create a basic `<code>.eslintrc.json</code>` file with the following content:</p> <p>{<br /> “extends”: [“eslint:recommended”],<br /> “parserOptions”: {<br /> “ecmaVersion”: 2021<br /> },<br /> “rules”: {<br /> // Your custom rules here<br /> }<br />}<br />```</p> <p><strong>Step 4: Configure Prettier</strong></p> <p>Create a <code>Prettier</code> configuration file in your project’s root directory. You can create a `<code>.prettierrc.json</code>` file with your preferred formatting options:</p> <p>{<br /> “singleQuote”: true,<br /> “trailingComma”: “es5”,<br /> “tabWidth”: 2<br />}</p> <p><strong>Step 5: Configure Husky</strong></p> <p>Husky enables us to run scripts (such as <code>ESLint</code> and <code>Prettier</code>) automatically before committing code. To set up <code>Husky</code>, add the following to your <code>package.json</code>:</p> <p>{<br /> “husky”: {<br /> “hooks”: {<br /> “pre-commit”: “lint-staged”<br /> }<br /> },<br /> “lint-staged”: {<br /> “*.js”: [<br /> “eslint — fix”,<br /> “prettier — write”,<br /> “git add”<br /> ]<br /> }<br />}</p> <p><strong>Step 6: Create Sample Code</strong></p> <p>Create a sample JavaScript file <code>index.js</code> in your project’s root directory:</p> <p>const greeting = “Hello, world!”;<br />console.log(greeting);</p> <p><strong>Step 7: Test the Setup</strong></p> <p>Now that everything is set up, let’s test it:</p> <p>1. Make sure you’re in your project’s root directory.<br />2. Modify <code>index.js</code> to introduce a linting error, like removing a semicolon.<br />3. Try to commit the changes using `git commit -am “Test commit”`.</p> <p>Husky should prevent the commit due to the linting error, and <code>ESLint</code> will automatically fix the issue and format the code using Prettier.</p> <p><strong>Advanced Configuration</strong></p> <p>To further customize your setup, you can:</p> <p>- Extend ESLint rules in your `.<code>eslintrc.json</code>` file.<br />- Explore additional Prettier options in your `<code>.prettierrc.json` </code> file.<br />- Add more pre-commit tasks to `<code>lint-staged</code>` in your `<code>package.json</code>`.</p> <p>By following this tutorial, you’ve successfully set up Husky, ESLint, and Prettier in your <code>Node.js</code> application. These tools will help you maintain consistent code quality and enhance the development experience for you and your team.</p> Integrating GitHub OAuth with Passport.js: Handling Email Validationhttps://blog.mdadul.dev/integrating-github-oauth-with-passport-js-handling-email-validation-869a56dfedf1/https://blog.mdadul.dev/integrating-github-oauth-with-passport-js-handling-email-validation-869a56dfedf1/Photo by Gabriel Heinzer on Unsplash Introduction:OAuth authentication is a powerful way to enable users to log into your application using their existing accounts on popular platforms like Google and GitHub. Passport.js, a widely-used authentication...Wed, 09 Aug 2023 12:46:05 GMT<p>Photo by <a target="_blank" href="https://unsplash.com/@6heinz3r?utm_source=medium&amp;utm_medium=referral">Gabriel Heinzer</a> on <a target="_blank" href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></p> <p><strong>Introduction:</strong><br />OAuth authentication is a powerful way to enable users to log into your application using their existing accounts on popular platforms like Google and GitHub. Passport.js, a widely-used authentication middleware for Node.js, simplifies the process of implementing various authentication strategies, including OAuth. In this tutorial, we’ll delve into integrating GitHub OAuth with Passport.js, specifically addressing the challenge of handling email validation.</p> <p><strong>Prerequisites:</strong><br />Before you begin, make sure you have a basic understanding of OAuth authentication, <code>Node.js</code>, and npm packages. Familiarity with GitHub OAuth and Passport.js will be beneficial for following this tutorial.</p> <p><strong>GitHub OAuth Integration and Email Retrieval:</strong></p> <p>In many OAuth authentication scenarios, you want to gather essential user information, such as their email, to ensure a seamless user experience. GitHub OAuth, however, introduces a unique challenge as not all users make their email publicly available on their profiles. This challenge necessitates a careful approach to ensure you retrieve accurate and valid email data.</p> <p>To address this challenge, we’ll walk through the process of integrating GitHub OAuth with Passport.js and using the GitHub API to fetch the user’s email. We’ll focus on the following steps:</p> <p><strong>1. Setting Up Passport.js and GitHub OAuth:</strong><br /> Begin by creating a new Node.js application and installing the necessary dependencies, including Passport.js and the GitHub OAuth strategy. You’ll need to register your application on GitHub and obtain the <code>clientID</code> and <code>clientSecret</code>.</p> <p>Install the required packages:</p> <p> npm install passport passport-github2 node-fetch</p> <p>Create the Passport.js configuration file and set up the GitHub strategy.</p> <p><strong>2. Fetching User Email from GitHub API:</strong><br />To retrieve the user’s email from GitHub, we’ll use the GitHub API. Specifically, we’ll make a request to the `/user/emails` endpoint using the user’s access token. This approach ensures accurate and up-to-date email information.</p> <p><strong>Integrate the `node-fetch` package to handle API requests.</strong></p> <p> const fetch = require(‘node-fetch’); </p> <p>// Inside GitHub strategy callback<br /> try {<br /> const emailResponse = await fetch(‘https://api.github.com/user/emails', {<br /> headers: {<br /> Authorization: `Bearer ${accessToken}`,<br /> ‘User-Agent’: ‘Your-App-Name’,<br /> },<br /> });<br /> const emailData = await emailResponse.json(); </p> <p> // Find the primary email<br /> const primaryEmail = emailData.find(email =&gt; email.primary); </p> <p> if (primaryEmail) {<br /> // Proceed with user registration or authentication<br /> } else {<br /> console.error(‘No primary email found.’);<br /> done(null, false);<br /> }<br /> } catch (error) {<br /> console.error(error.message);<br /> done(null, false);<br /> }</p> <p><strong>3. Integrating Email Retrieval with Registration</strong><br />After fetching the user’s primary email from the GitHub API, integrate it with your user registration process. Create a new user record in your database using the retrieved email and other profile information.</p> <p> const userData = {<br /> firstName: profile._json.name.toLowerCase(),<br /> lastName: profile._json.name.toLowerCase(),<br /> email: primaryEmail.email, // Use the email from GitHub API response<br /> picture: profile._json.avatar_url,<br /> isConfirmed: true,<br /> };<br /> const user = await User.findOne({ email: userData.email }).select('-password');<br /> if (user) {<br /> return user;<br /> }<br /> const newUser = await User.create(userData);<br /> done(null, user);</p> <p><strong>Conclusion</strong>: Integrating GitHub OAuth with Passport.js and handling email validation might initially seem like a complex task, but with the right approach, it becomes a valuable addition to your authentication strategy. By using the GitHub API to retrieve user email information, you ensure that your application obtains accurate and up-to-date data, leading to a more reliable and user-friendly experience for your users.</p> <p>As you continue to develop and refine your authentication flow, the skills you’ve gained from this tutorial will prove beneficial. By addressing email validation challenges, you’re not only enhancing your application’s security but also creating a smoother onboarding process for your users.</p> <p><em>In this tutorial, you’ve learned how to integrate GitHub OAuth with Passport.js while overcoming the challenge of email validation. By utilizing the GitHub API to fetch user email information directly, you’ve gained insights into a powerful approach to handling OAuth authentication. As you advance in your development journey, remember that continuous learning and problem-solving are key to mastering complex authentication scenarios and building robust applications.</em></p> <p><em>Thank you for following along with this tutorial. Feel free to explore further customization options, refine your authentication flow, and delve into other authentication strategies supported by Passport.js. Happy coding!</em></p> Using Git Bash with Multiple GitHub Accounts using SSH Keyshttps://blog.mdadul.dev/using-git-bash-with-multiple-github-accounts-using-ssh-keys-cbfe386a0f1a/https://blog.mdadul.dev/using-git-bash-with-multiple-github-accounts-using-ssh-keys-cbfe386a0f1a/Learn to configure Git Bash for multiple GitHub accounts using SSH keys. Seamlessly switch accounts without the hassle of logging in and outSun, 06 Aug 2023 02:47:42 GMT<p>Photo by <a target="_blank" href="https://unsplash.com/@synkevych?utm_source=medium&amp;utm_medium=referral">Roman Synkevych</a> on <a target="_blank" href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></p> <p><strong>Introduction</strong>: If you’re new to the world of version control and collaborating on GitHub, you might be wondering how to handle multiple GitHub accounts on the same computer efficiently. Fortunately, with a simple setup using SSH keys, you can seamlessly switch between different GitHub accounts without the hassle of logging in and out repeatedly. In this beginner-friendly guide, we’ll walk you through the step-by-step process of configuring Git Bash to work with two GitHub accounts using SSH keys.</p> <h3 id="heading-step-1-generating-ssh-keys"><strong>Step 1: Generating SSH Keys</strong></h3> <p>SSH keys are secure cryptographic keys that allow you to authenticate with GitHub without the need for a username and password. Let’s generate SSH keys for each of your GitHub accounts:</p> <p><strong>1. Open Git Bash:</strong> First, launch Git Bash on your computer. If you haven’t installed Git Bash yet, you can download it from the official Git website and install it following the installation wizard.</p> <p><strong>2. Generate SSH Keys:</strong> In the Git Bash terminal, type the following commands, replacing <code>&lt;Email_1&gt;</code> and <code>&lt;Email_2</code> with the email addresses associated with your GitHub accounts:</p> <p>For Account 1:</p> <p><code>ssh-keygen -t ed25519 -C “&lt;EMAIL_1&gt;”</code></p> <p>For Account 2:</p> <p><code>ssh-keygen -t ed25519 -C “&lt;EMAIL_2&gt;”</code></p> <p>Press Enter to run the command. You will be prompted to save the keys in the default location (~/.ssh/) with filenames like <code>id_ed25519</code> and <code>id_ed25519.pub</code>.</p> <p><strong>Step 2: Adding SSH Keys to GitHub</strong><br />To use your newly generated SSH keys for authentication on GitHub, you need to add them to your GitHub accounts:</p> <p>1. Copy the Public Key: Run the following commands in Git Bash to copy the public keys for each GitHub account:</p> <p>For Account 1:</p> <p><code>clip &lt; ~/.ssh/id_ed25519.pub</code></p> <p>For Account 2:</p> <p><code>clip &lt; ~/.ssh/id_ed25519.pub</code></p> <p>The public keys are now copied to your clipboard.</p> <p>2. Adding Keys to GitHub: Log in to your GitHub Account 1 in your web browser. Then, go to “<strong>Settings” -&gt; “SSH and GPG keys” -&gt; “New SSH key”</strong> and paste the key you copied earlier.</p> <p>3. Repeat the above step for Account 2, but this time, log in to your GitHub Account 2.</p> <p><strong>Step 3: Configuring SSH for Multiple Accounts</strong><br />Next, we need to configure SSH to differentiate between the two GitHub accounts. We’ll create or edit the SSH configuration file to accomplish this:</p> <p>1. Create the Configuration File: In Git Bash, type the following command to create the SSH configuration file (if it doesn’t exist):</p> <p><code>touch ~/.ssh/config</code></p> <p>2. Edit the Configuration File: Open the configuration file using a text editor. We’ll use the Nano editor for this example:</p> <p><code>nano ~/.ssh/config</code></p> <p>Add the following lines to the configuration file, replacing <code>Email_1</code> and <code>Email_2</code> with the corresponding email addresses used to generate the keys:</p> <pre><code class="lang-apache"><span class="hljs-comment"># Account 1</span> <span class="hljs-attribute">Host</span> github.com <span class="hljs-attribute">HostName</span> github.com <span class="hljs-attribute">User</span> git <span class="hljs-attribute">IdentityFile</span> ~/.ssh/id_ed<span class="hljs-number">25519</span> <span class="hljs-comment"># Account 2</span> <span class="hljs-attribute">Host</span> github.com-acc<span class="hljs-number">2</span> <span class="hljs-attribute">HostName</span> github.com <span class="hljs-attribute">User</span> git <span class="hljs-attribute">IdentityFile</span> ~/.ssh/id_ed<span class="hljs-number">25519</span>.pub </code></pre> <p><em>Save and exit the text editor (for Nano, press `Ctrl+O`, then `Ctrl+X`).</em></p> <p><strong>Step 4: Configuring Repository URLs</strong><br />Finally, to complete the setup, we need to adjust the remote URLs of the repositories associated with each GitHub account:</p> <ol> <li>For Account 1’s repositories, run the following command, replacing `username` with your Account 1 GitHub username and `repo` with the repository name:</li> </ol> <p><code>git remote set-url origin [email protected]:username/repo.git</code></p> <p>2. For Account 2’s repositories, run the following command, replacing <code>username</code> with your Account 2 GitHub username and <code>repo</code> with the repository name:</p> <p><code>git remote set-url origin [email protected]:username/repo.git</code></p> <p><strong>Conclusion</strong>:<br />Congratulations! You have successfully set up Git Bash to work with two GitHub accounts using SSH keys. Now, you can seamlessly switch between your accounts without any hassle, making version control and collaboration a breeze. As you continue your journey with Git and GitHub, this setup will empower you to work efficiently and securely with multiple accounts simultaneously. Happy coding!</p> Oracle Cheat Sheethttps://blog.mdadul.dev/oracle-cheat-sheet-d0ce930faf47/https://blog.mdadul.dev/oracle-cheat-sheet-d0ce930faf47/Oracle Logo Oracle is one of the most popular relational database management systems in the world, used by many organizations to store and manage their data. SQL is the standard language used to interact with Oracle databases, allowing users to retri...Sun, 07 May 2023 02:26:50 GMT<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771086460083/d7fe038d-de87-43e1-a67f-44645f9d7e03.png" alt="Oracle logo" /></p> <p>Oracle Logo</p> <p>Oracle is one of the most popular relational database management systems in the world, used by many organizations to store and manage their data. SQL is the standard language used to interact with Oracle databases, allowing users to retrieve, modify, and manipulate data. However, even experienced Oracle users may need a quick reference guide for frequently used SQL commands. In this blog post, we’ll provide a cheat sheet of commonly used Oracle SQL commands that can help you write more efficient and effective SQL queries.</p> <p><strong>SELECT</strong></p> <p>The SELECT statement is used to retrieve data from one or more tables.</p> <p>SELECT column1, column2, ...<br />FROM table_name;</p> <p><strong>WHERE</strong></p> <p>The WHERE clause is used to filter data based on certain criteria.</p> <p>SELECT column1, column2, ...<br />FROM table_name<br />WHERE condition;</p> <p><strong>GROUP BY</strong></p> <p>The GROUP BY clause is used to group rows that have the same values.</p> <p>SELECT column1, COUNT(column2)<br />FROM table_name<br />GROUP BY column1;</p> <p><strong>ORDER BY</strong></p> <p>The ORDER BY clause is used to sort the results in ascending or descending order.</p> <p>SELECT column1, column2, ...<br />FROM table_name<br />ORDER BY column1 DESC;</p> <p><strong>JOIN</strong></p> <p>The JOIN clause is used to combine rows from two or more tables based on a related column.</p> <p>SELECT column1, column2, ...<br />FROM table1<br />JOIN table2<br />ON table1.column = table2.column;</p> <p><strong>INSERT</strong></p> <p>The INSERT statement is used to insert new rows into a table.</p> <p>INSERT INTO table_name (column1, column2, ...)<br />VALUES (value1, value2, ...);</p> <p><strong>UPDATE</strong></p> <p>The UPDATE statement is used to modify existing rows in a table.</p> <p>UPDATE table_name<br />SET column1 \= value1, column2 \= value2, ...<br />WHERE condition;</p> <p><strong>DELETE</strong></p> <p>The DELETE statement is used to delete existing rows from a table.</p> <p>DELETE FROM table_name<br />WHERE condition;</p> <p>Whether you’re a beginner or an experienced Oracle user, having a quick reference guide for commonly used SQL commands can save you time and improve your productivity. In this blog post, we’ve provided a cheat sheet of frequently used Oracle SQL commands, including SELECT, WHERE, GROUP BY, ORDER BY, JOIN, INSERT, UPDATE, and DELETE. While this is not an exhaustive list of all SQL commands, it covers many of the fundamental commands that every Oracle user should be familiar with. By mastering these commands, you’ll be able to write more efficient and effective SQL queries, and make the most of your Oracle database.</p> MongoDB Cheat Sheethttps://blog.mdadul.dev/mongodb-cheat-sheet-6f5abb6bcc9c/https://blog.mdadul.dev/mongodb-cheat-sheet-6f5abb6bcc9c/MongoDB Logo MongoDB is a popular NoSQL database used in modern web applications. With its flexible document-based data model and scalability, it has become a go-to database for many developers. However, with its many features and options, it can be ...Sun, 30 Apr 2023 11:34:55 GMT<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771086463831/e768fc3f-d803-435e-b236-b5ce099c4808.png" alt /></p> <p>MongoDB Logo</p> <p>MongoDB is a popular NoSQL database used in modern web applications. With its flexible document-based data model and scalability, it has become a go-to database for many developers. However, with its many features and options, it can be overwhelming to remember all the commands and syntax. That’s why a MongoDB cheat sheet can be a handy resource for developers who want to quickly reference the essential commands and operators.</p> <ol> <li><p><strong>Installation</strong></p> </li> <li><p>Download MongoDB from the official website</p> </li> <li>Install MongoDB on your system</li> <li>Start the MongoDB server using the command: <code>mongod</code></li> </ol> <p>2. <strong>Basic commands:</strong></p> <ul> <li>Show all databases: <code>show dbs</code></li> <li>Select a database: <code>use &lt;database&gt;</code></li> <li>Show all collections in the current database: <code>show collections</code></li> <li>Insert a document into a collection: <code>db.&lt;collection&gt;.insertOne(&lt;document&gt;)</code></li> <li>Find all documents in a collection: <code>db.&lt;collection&gt;.find()</code></li> <li>Find documents that match a specific condition: <code>db.&lt;collection&gt;.find(&lt;query&gt;)</code></li> <li>Update a document: <code>db.&lt;collection&gt;.updateOne(&lt;filter&gt;, &lt;update&gt;)</code></li> <li>Delete a document: <code>db.&lt;collection&gt;.deleteOne(&lt;filter&gt;)</code></li> <li>Delete all documents in a collection: <code>db.&lt;collection&gt;.deleteMany({})</code></li> </ul> <p>3. <strong>Query Operators:</strong></p> <ul> <li>Comparison Operators: <code>$eq</code>, <code>$ne</code>, <code>$lt</code>, <code>$lte</code>, <code>$gt</code>, <code>$gte</code></li> <li>Logical Operators: <code>$and</code>, <code>$or</code>, <code>$not</code></li> <li>Element Operators: <code>$exists</code>, <code>$type</code></li> <li>Array Operators: <code>$in</code>, <code>$nin</code>, <code>$all</code>, <code>$size</code></li> </ul> <p>4. <strong>Aggregation</strong>:</p> <ul> <li>Group documents by a specific field: <code>db.&lt;collection&gt;.aggregate([{ $group: { _id: &lt;field&gt;, &lt;accumulator&gt; } }])</code></li> <li>Sort documents by a specific field: <code>db.&lt;collection&gt;.find().sort({ &lt;field&gt;: 1/-1 })</code></li> <li>Limit the number of documents returned: <code>db.&lt;collection&gt;.find().limit(&lt;limit&gt;)</code></li> <li>Skip a certain number of documents: <code>db.&lt;collection&gt;.find().skip(&lt;skip&gt;)</code></li> <li>Count the number of documents in a collection: <code>db.&lt;collection&gt;.count()</code></li> </ul> <p>5.<strong>Indexing:</strong></p> <ul> <li>Create an index: <code>db.&lt;collection&gt;.createIndex({ &lt;field&gt;: 1/-1 })</code></li> <li>Show all indexes in a collection: <code>db.&lt;collection&gt;.getIndexes()</code></li> <li>Remove an index: <code>db.&lt;collection&gt;.dropIndex({ &lt;field&gt;: 1/-1 })</code></li> </ul> <p>In conclusion, a MongoDB cheat sheet can be an incredibly useful resource for developers who work with MongoDB on a regular basis. It provides a quick reference for the essential commands and operators needed to interact with the database. By keeping this cheat sheet handy, developers can increase their productivity and reduce the time spent searching for commands and syntax. Whether you’re a beginner or an experienced MongoDB user, a cheat sheet can be a valuable tool to have in your arsenal.</p> Guideline for reading papers.https://blog.mdadul.dev/guideline-for-reading-papers-cc436e1a322d/https://blog.mdadul.dev/guideline-for-reading-papers-cc436e1a322d/Reading a paper can be a daunting task, especially if you are new to the field or the paper is particularly technical. Here is a step-by-step guideline that you can follow to read a paper: Start with the title and abstract: The title and abstract ca...Thu, 06 Apr 2023 13:34:43 GMT<p>Reading a paper can be a daunting task, especially if you are new to the field or the paper is particularly technical. Here is a step-by-step guideline that you can follow to read a paper:</p> <ol> <li><strong>Start with the title and abstract:</strong> The title and abstract can give you a quick idea of what the paper is about. They can also help you decide if the paper is relevant to your interests.</li> <li><strong>Skim the introduction:</strong> The introduction can provide you with the background information on the topic and explain the motivation for the research. Skimming through the introduction can give you a general idea of what the paper is about.</li> <li><strong>Read the conclusion:</strong> The conclusion can summarize the main findings and the contribution of the paper. Reading the conclusion can give you an overview of the paper’s main arguments and findings.</li> <li><strong>Scan the headings and subheadings:</strong> The headings and subheadings can help you understand the structure of the paper and the main sections. Scanning them can help you navigate through the paper.</li> <li><strong>Read the figures and tables:</strong> Figures and tables can provide a visual representation of the data and the findings. Reading the figures and tables can help you understand the main results and the methodology.</li> <li><strong>Read the methodology and results sections:</strong> The methodology and results sections can provide you with the details of the study design, the data collection, and the analysis. These sections can help you evaluate the quality of the study and the validity of the results.</li> <li><strong>Read the discussion:</strong> The discussion can interpret the findings, explain the implications, and suggest future research directions. Reading the discussion can help you understand the significance of the paper and the contribution to the field.</li> <li><strong>Take notes:</strong> Taking notes can help you remember the main points and the key findings of the paper. You can also jot down any questions or comments you may have.</li> <li><strong>Reflect and discuss:</strong> After reading the paper, take some time to reflect on the main points and the contribution to the field. You can also discuss the paper with your peers or colleagues to get their opinions and feedback.</li> </ol> <p>By following these steps, you can read a paper effectively and efficiently. Remember, reading a paper is a skill that can be developed with practice and patience.</p> Mongoose Cheat Sheethttps://blog.mdadul.dev/mongoose-cheat-sheet-1d66118de3bd/https://blog.mdadul.dev/mongoose-cheat-sheet-1d66118de3bd/mongoose If you’re working with Mongoose, the popular object data modeling (ODM) library for MongoDB in Node.js, having a quick reference guide or “cheat sheet” can be incredibly helpful. In this blog post, we provide a concise summary of some of the...Sat, 01 Apr 2023 07:51:36 GMT<p>mongoose</p> <p>If you’re working with Mongoose, the popular object data modeling (ODM) library for MongoDB in Node.js, having a quick reference guide or “cheat sheet” can be incredibly helpful. In this blog post, we provide a concise summary of some of the most commonly used Mongoose methods and syntax, organized by category, to help you work more efficiently and effectively with this powerful ODM.</p> <p><strong>Schema definition</strong></p> <p>const { Schema } = require('mongoose');<br />const userSchema = new Schema({<br /> name: String,<br /> email: { type: String, unique: true },<br /> age: Number,<br /> isAdmin: { type: Boolean, default: false },<br /> createdAt: { type: Date, default: Date.now }<br />}); </p> <p><strong>Model definition</strong></p> <p>const mongoose = require('mongoose');<br />const userSchema = require('./userSchema');<br />const User = mongoose.model('User', userSchema);<br />Creating a document<br />const user = new User({<br /> name: 'John Doe',<br /> email: '[email protected]',<br /> age: 30<br />});<br />user.save(); </p> <p><strong>Finding documents</strong></p> <p>// Find all documents<br />const users = await User.find();<br />// Find documents that match a query<br />const admins = await User.find({ isAdmin: true });<br />// Find a single document by ID<br />const user = await User.findById(userId);</p> <p><strong>Updating documents</strong></p> <p>// Update a single document by ID<br />await User.findByIdAndUpdate(userId, { name: 'Jane Doe' });<br />// Update multiple documents that match a query<br />await User.updateMany({ isAdmin: true }, { isAdmin: false });</p> <p><strong>Deleting documents</strong></p> <p>// Delete a single document by ID<br />await User.findByIdAndDelete(userId);<br />// Delete multiple documents that match a query<br />await User.deleteMany({ isAdmin: false });</p> <p><strong>Validation</strong></p> <p>const userSchema = new Schema({<br /> name: { type: String, required: true },<br /> email: { type: String, required: true, unique: true },<br /> age: { type: Number, min: 18 },<br /> isAdmin: { type: Boolean, default: false },<br /> createdAt: { type: Date, default: Date.now }<br />});</p> <p>In Mongoose, you can define relationships between models using references or subdocuments.</p> <p><strong><em>Using references:</em></strong><br />To define a relationship between two models using references, you can use the ref property in a field definition. The ref property specifies the name of the target model. Here’s an example:</p> <p>const userSchema = new mongoose.Schema({<br /> name: String,<br /> posts: [{<br /> type: mongoose.Schema.Types.ObjectId,<br /> ref: 'Post'<br /> }]<br />});</p> <p>const postSchema = new mongoose.Schema({<br /> title: String,<br /> content: String,<br /> author: {<br /> type: mongoose.Schema.Types.ObjectId,<br /> ref: 'User'<br /> }<br />});</p> <p>In this example, we have two models: <code>User</code> and <code>Post</code>. The User model has a field called posts, which is an array of <code>ObjectId</code> values that reference <code>Post</code>documents. The Post model has a field called author, which is an <code>ObjectId</code>value that references a User document.</p> <p>To populate the posts field of a User document with the corresponding Post documents, you can use the populate() function:</p> <p>const user = await User.findById(userId).populate('posts');</p> <p>This will retrieve the User document with the specified _id value and then retrieve the Post subdocument with the specified <strong>postId</strong> value.</p>