<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Gideon Akinsanmi - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Gideon Akinsanmi - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 18 Jun 2026 05:24:27 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/GidAkinsanmi/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Secure a Next.js AI Application Deployed on Vercel ]]>
                </title>
                <description>
                    <![CDATA[ In this in-depth guide, I’ll be showing how to secure a Next.js AI app deployed on Vercel. We’ll be taking a hands-on approach by starting with a simple AI app riddled with vulnerabilities. This article will guide you through how you can detect vulne... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-secure-a-nextjs-ai-application-deployed-on-vercel/</link>
                <guid isPermaLink="false">66c3b3ec1528ee1a2589e593</guid>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Security ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gideon Akinsanmi ]]>
                </dc:creator>
                <pubDate>Mon, 19 Aug 2024 21:06:52 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1724061409083/f9df1023-0bf0-4dc4-b97b-041738bfe5f8.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this in-depth guide, I’ll be showing how to secure a Next.js AI app deployed on Vercel. We’ll be taking a hands-on approach by starting with a simple AI app riddled with vulnerabilities. This article will guide you through how you can detect vulnerabilities and apply fixes in an existing Next.js AI app.</p>
<p>This tutorial covers different areas of software development like full-stack, cloud development, and third-party integration. You’ll learn how to use tools like Git/GitHub, Next.js, and Vercel to improve the security of an AI app.</p>
<p>Also, if you prefer watching actual implementations, there are YouTube videos at the end of most sections to show you how it's done.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-getting-started">Getting started</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-exploring-the-project-files">Exploring the project files</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-the-layouttsx-file">The layout.tsx file</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-pagetsx-file">The page.tsx file</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-hooksuseopenaits-file">The hooks/useOpenAI.ts file</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-packagejson-file">The package.json file</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-get-your-openai-api-keys">How to Get Your OpenAI API Keys</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-deploy-the-project-on-vercel">How to Deploy the Project on Vercel</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-vulnerability-one-exposure-of-sensitive-data-in-the-frontend">Vulnerability one: Exposure of sensitive data in the frontend</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-vulnerability-two-dos-and-ddos-attacks">Vulnerability two: DOS or DDOS attacks</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-rate-limiting">Rate limiting</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-vercel-and-cloudflare-ddos-protection">Vercel and Cloudflare DDOS protection</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-vercel-security-features">Vercel security features</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-vulnerability-3-no-authentication-and-authorization">Vulnerability 3: No authentication and authorization</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-optimizing-the-code">Optimizing the code</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>Before you begin this tutorial, here are some of the things you need to have:</p>
<ul>
<li><p>A basic knowledge of writing <a target="_blank" href="https://nextjs.org">Next.js</a> code</p>
</li>
<li><p>A code editor (like VSCode) for writing and editing the project’s source code.</p>
</li>
<li><p><a target="_blank" href="https://git-scm.com">Git</a> and <a target="_blank" href="https://github.com">GitHub</a> account for version control, continuous integration, and authentication.</p>
</li>
<li><p>A <a target="_blank" href="https://vercel.com">Vercel</a> account to host the project.</p>
</li>
<li><p>An <a target="_blank" href="https://platform.openai.com">OpenAI Developer</a> account to get your API key.</p>
</li>
</ul>
<h2 id="heading-getting-started">Getting Started</h2>
<p>First, you need to clone this <a target="_blank" href="https://github.com/Gidthecoder/nextjs-ai">GitHub repository</a>.</p>
<p>I created the repo specifically for this tutorial to show you some of the vulnerabilities that you might overlook when building a Next.js AI app.</p>
<p>To clone the project, open your command line interface and navigate to the folder you want the repo to be in.</p>
<p>Next, use the <code>git clone</code> command to clone the project.</p>
<pre><code class="lang-plaintext">git clone https://github.com/Gidthecoder/nextjs-ai.git
</code></pre>
<p>Remember that you must have already set up Git in your system or the command won’t work.</p>
<p>Once that's done, navigate to the folder using the command below:</p>
<pre><code class="lang-plaintext">cd nextjs-ai
</code></pre>
<p>Next, type the command below to install the project dependencies:</p>
<pre><code class="lang-plaintext">npm install
</code></pre>
<p>Once this is done, you can open the folder in your text editor and explore the files.</p>
<p>The code is well-commented, so you shouldn't have much problem understanding it.</p>
<p>To run the project, first make sure you’re in the project directory in your CLI. Then run the command below:</p>
<pre><code class="lang-plaintext">npm run dev
</code></pre>
<p>If everything works well, open your browser and type <code>http://localhost:3000/</code> in the search bar.</p>
<p>The result from the browser should look this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724021933164/2abbc405-c90b-44d1-bd8c-d2c233f42062.png" alt="Image showing the project displayed in the browser" class="image--center mx-auto" width="1913" height="919" loading="lazy"></p>
<p>If you got the same result, give yourself a round of applause. Great job.</p>
<p>If you try sending a message from the form, you’ll notice that an error message is displayed. This is because you haven’t added an OpenAI API key to the project.</p>
<p>But don’t worry about that for now. Let’s have a look at the project files.</p>
<p>Here is the video for this section:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/ataC9zP6aL0" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-exploring-the-project-files"><strong>Exploring the Project Files</strong></h2>
<p>The project we cloned is basically a ChatGPT wrapper. You ask it a question and it gives you a response.</p>
<p>Under the hood, the project uses Next.js to send an API request to the OpenAI server and displays the response to the user.</p>
<p>If you opened the folder through VSCode, the project structure should look like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724054546731/203d0875-1694-48ed-9ed4-a92f37b887fb.png" alt="Image showing the folder structure of the project" class="image--center mx-auto" width="535" height="719" loading="lazy"></p>
<p>The project has 4 top-level folders: <code>.next</code>, <code>node_modules</code>, <code>public</code>, and <code>src</code>.</p>
<p>The files in the root folder include <code>.gitignore</code>, <code>package.json</code>, <code>tailwind.config.js</code>, <code>tsconfig.json</code>, and others.</p>
<p>Most of your work will be within the <code>src/app</code> folder because it contains all the code you need for the project.</p>
<p>The app folder represents the app router. It contains <code>layout.tsx</code>, <code>page.tsx</code>, <code>global.css</code>, <code>hooks</code> folder, and so on.</p>
<h3 id="heading-the-layouttsx-file"><strong>The layout.tsx file</strong></h3>
<p><code>layout.tsx</code> is a TypeScript file that contains the code for root layout.</p>
<p>From lines 1 to 3, you'll import the <code>Metadata</code> type, <code>Inter</code> font, and a <code>globals.css</code> file that allows you to use TailwindCSS utility classes.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> type { Metadata } <span class="hljs-keyword">from</span> <span class="hljs-string">"next"</span>;

<span class="hljs-keyword">import</span> { Inter } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/font/google"</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">"./globals.css"</span>;
</code></pre>
<p>From line 5 to 10, 2 you'll create the variables <code>inter</code> and <code>metadata</code>. <code>inter</code> initializes the <code>Inter</code> font, and <code>metadata</code> stores an object that contains the title and description of the site, similar to the HTML title and meta elements.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> inter = Inter({ <span class="hljs-attr">subsets</span>: [<span class="hljs-string">"latin"</span>] });

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> metadata: Metadata = {
  <span class="hljs-attr">title</span>: <span class="hljs-string">"WriterAI"</span>,
  <span class="hljs-attr">description</span>: <span class="hljs-string">"A ChatGPT wrapper that answers your questions"</span>,
};
</code></pre>
<p>Next, you'll create the <code>RootLayout</code> component that'll wrap all the pages of the project.</p>
<pre><code class="lang-javascript"><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">RootLayout</span>(<span class="hljs-params">{
  children,
}: Readonly&lt;{
  children: React.ReactNode;
}&gt;</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{inter.className}</span>&gt;</span>
        {children}
      <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span></span>
);
}
</code></pre>
<h3 id="heading-the-pagetsx-file"><strong>The page.tsx file</strong></h3>
<p><code>app/page.tsx</code> is a TypeScript file that represents the code that’ll be rendered in the browser when the root URL (/) is called. </p>
<p>Line 1 has a <code>use client</code> directive which declares the file as a client component. This way we are able to fully use React.js features in the component.</p>
<pre><code class="lang-javascript"><span class="hljs-string">"use client"</span>;
</code></pre>
<p>From line 3 to 6, you'll import a custom <code>useOpenAI</code> hook and the React built-in hooks (<code>useState</code>, <code>useRef</code>, <code>useEffect</code>, … ).</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//import the custom hook for getting the response from the OpenAI API</span>
<span class="hljs-keyword">import</span> useOpenAI <span class="hljs-keyword">from</span> <span class="hljs-string">'./hooks/useOpenAI'</span>;

<span class="hljs-keyword">import</span> {useState, useRef, useEffect, FormEvent} <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
</code></pre>
<p>Then you'll create a custom <code>Message</code> type that’ll be used with the state that stores the messages between the user and the OpenAI API.</p>
<pre><code class="lang-javascript">type Message = {
  <span class="hljs-attr">role</span>: string;
  content: string;
};
</code></pre>
<p>Lines 13 to 120 contain the code for the <code>Home</code> component that’ll be rendered in the browser when the root URL is called.</p>
<p>Within the <code>Home</code> component, line 15 initializes the <code>useOpenAI</code> hook.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//initialize the custom hook</span>
<span class="hljs-keyword">const</span> getCompletion = useOpenAI();
</code></pre>
<p>Line 17 initializes a <code>useRef</code> hook that’ll be used to reference the DOM container element that wraps the messages between the user and the server.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> chatContainerRef = useRef&lt;HTMLDivElement&gt;(<span class="hljs-literal">null</span>);
</code></pre>
<p>From lines 19 to 23, you're creating a <code>content</code> variable that represents an array that’ll be used as the initial values for the state that stores the chat/messages.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//initial chats for the site</span>
 <span class="hljs-keyword">let</span> content: Message[]  = [
   {<span class="hljs-attr">role</span>: <span class="hljs-string">"user"</span>, <span class="hljs-attr">content</span>: <span class="hljs-string">"Are you ready to write about any topic for me"</span>},
   {<span class="hljs-attr">role</span>: <span class="hljs-string">"assistant"</span>, <span class="hljs-attr">content</span>: <span class="hljs-string">"Always ready bruv. what is your topic?"</span>}
]
</code></pre>
<p>Lines 25 to 30 represent the state variables of the component. <code>Input</code> keeps track of the text entered in the input element by the user. <code>chats</code> store the messages from the user and OpenAI API. It is initialized with the <code>content</code> variable. <code>isTyping</code> keeps track of when the user is typing. Its initial value is <code>false</code>.</p>
<pre><code class="lang-javascript"> <span class="hljs-comment">//this state stores the input value</span>
 <span class="hljs-keyword">let</span> [input, setInput] = useState&lt;string&gt;(<span class="hljs-string">''</span>);
 <span class="hljs-comment">//this state stores the chats</span>
 <span class="hljs-keyword">let</span> [chats, setChats] = useState&lt;Message[]&gt;(content);
 <span class="hljs-comment">//this state keeps track of when the AI is typing</span>
 <span class="hljs-keyword">let</span> [isTyping, setIsTyping] = useState&lt;boolean&gt;(<span class="hljs-literal">false</span>);
</code></pre>
<p>The code in lines 31 to 72 represents the <code>handlerChat</code> function which is the event handler that is called any time a user hits the enter key or ask button in the form.</p>
<p>In summary, its job is to receive the prompt from the form, update the <code>input</code>, <code>isTyping</code>, and <code>chats</code> states, pass the prompt to the <code>getCompletion</code> function of the <code>useOpenAI</code> hook, wait for the response, and display it to the user.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//handleChat event handler for the submit event</span>
<span class="hljs-keyword">let</span> handleChat = <span class="hljs-keyword">async</span> (prompt: string, <span class="hljs-attr">e</span>: FormEvent) =&gt; {
  <span class="hljs-comment">//prevent the form from reloading the entire page when submitting</span>
  e.preventDefault();

  <span class="hljs-comment">//if there is no value in the input or it is clicked when the isTyping is true, do nothing</span>
  <span class="hljs-keyword">if</span> (!prompt || isTyping) <span class="hljs-keyword">return</span>;

  <span class="hljs-comment">//set isTyping state to true. 'true' adds an element displaying 'AI typing'</span>
  setIsTyping(<span class="hljs-literal">true</span>);

  <span class="hljs-comment">//clear the content of the input state. This also clears the input element which displays the value.</span>
  setInput(<span class="hljs-string">''</span>);

  <span class="hljs-comment">//updates the chats state with the prompt sent from the input</span>
  setChats(<span class="hljs-function"><span class="hljs-params">prevChats</span> =&gt;</span> {
    <span class="hljs-keyword">const</span> updatedChats = [...prevChats, { <span class="hljs-attr">role</span>: <span class="hljs-string">'user'</span>, <span class="hljs-attr">content</span>: prompt}];
    <span class="hljs-keyword">return</span> updatedChats;
  });

  <span class="hljs-keyword">try</span> {
    <span class="hljs-comment">//send the prompt through the openai api and wait for the response</span>
    <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> getCompletion(prompt);

    <span class="hljs-comment">//update the chat prompt with response gotten from the openai api</span>
    setChats(<span class="hljs-function"><span class="hljs-params">prevChats</span> =&gt;</span> {
      <span class="hljs-keyword">const</span> updatedChats = [...prevChats, <span class="hljs-built_in">Object</span>(result)];
      <span class="hljs-keyword">return</span> updatedChats;
     });

     <span class="hljs-comment">//set isTyping state to false. 'false' removes the element displaying 'AI typing'</span>
     setIsTyping(<span class="hljs-literal">false</span>)

  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-comment">//catch any possible error from the request</span>
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error fetching completion:"</span>, error);

    <span class="hljs-comment">//set isTyping state to false. 'false' removes the element displaying 'AI typing'</span>
    setIsTyping(<span class="hljs-literal">false</span>)
  }
}
</code></pre>
<p>Lines 73 to 78 contain the code in the <code>useEffect</code> hook. Any time the <code>chats</code> state is updated, the document will be scrolled to the bottom so that the most recent messages are displayed.</p>
<pre><code class="lang-javascript">useEffect(<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">if</span> (chatContainerRef.current) {
    <span class="hljs-comment">//whenever the chats state is updated, scroll to the bottom of the container element to display the recent messages</span>
    chatContainerRef.current.scrollTo({ <span class="hljs-attr">top</span>: chatContainerRef.current.scrollHeight, <span class="hljs-attr">behavior</span>: <span class="hljs-string">'smooth'</span> });
  }
}, [chats]);
</code></pre>
<p>The code from lines 81 to 120 contains the markup that’ll be rendered in the browser.</p>
<p>From lines 91 to 99, the content of the <code>chats</code> state is added to the markup using the <code>map</code> function. Messages from the AI are aligned to the left while those of the user are aligned to the right</p>
<pre><code class="lang-javascript">{
  <span class="hljs-comment">//the content of chats state is looped to display the content. if the content is from the user, it will be aligned to the right. if it's from the AI, it'll be aligned to the left</span>
  chats.map(<span class="hljs-function">(<span class="hljs-params">data, index</span>) =&gt;</span>
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">data.role</span> == <span class="hljs-string">'user'</span>? '<span class="hljs-attr">text-right</span>'<span class="hljs-attr">:</span> '<span class="hljs-attr">text-left</span>'} <span class="hljs-attr">my-</span>[<span class="hljs-attr">30px</span>]`}&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-[15px] bg-[#4d4d4dff] max-w-[60%] p-[10px] lg:p-[20px] rounded-xl text-left text-[#f2f2f2ff] inline-block"</span>&gt;</span>
        {data.content}
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  ))
}
</code></pre>
<p>From lines 101 to 107, there is a code block that is always rendered anytime the <code>isTyping</code> state (which is triggered anytime a user sends a message and is expecting a response) is <code>true</code>.</p>
<pre><code class="lang-javascript">{<span class="hljs-comment">/*if the isTyping state is true, display the element. if not, hide it.*/</span>}
&lt;div className={isTyping? <span class="hljs-string">'block'</span>: <span class="hljs-string">'hidden'</span>}&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-left my-[30px]'</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-[15px] bg-[#4d4d4dff] max-w-[60%] p-[10px] lg:p-[20px]  rounded-xl text-center text-[#f2f2f2ff] inline-block"</span>&gt;</span>
      AI Typing...
    <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
&lt;/div&gt;
</code></pre>
<p>Lines 113 to 116 contain the code that represents the form. Whenever the input element is changed, the <code>input</code> state will be updated. And any time the form is submitted (when the button is clicked or input is entered), the <code>handleChat</code> function is called, sending the value of <code>input</code> state and the form event as the arguments.</p>
<pre><code class="lang-javascript">{<span class="hljs-comment">/*when the form is submitted, activate a submit event that sends the value of the input and the event to the handleChat function */</span>}
&lt;form action=<span class="hljs-string">''</span> onSubmit={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> handleChat(input, e)}&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" lg:w-[50%] w-[70%] ml-[5%] lg:ml-[20%] p-[10px] outline-none bg-[#4d4d4dff] text-[15px] text-[#f2f2f2ff]"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'text'</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{input}</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">'Ask your questions'</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{</span> (<span class="hljs-attr">e</span>) =&gt;</span> setInput(e.target.value)}/&gt;</span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'py-[10px] px-[20px] bg-black text-[15px] text-[#f2f2f2ff]'</span>&gt;</span>Ask<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
&lt;/form&gt;
</code></pre>
<h3 id="heading-the-hooksuseopenaits-file"><strong>The hooks/useOpenAI.ts file</strong></h3>
<p>In the <code>hooks/useOpenAi.ts</code> file, you'll import the OpenAI library and store the API key in a variable.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> OpenAI <span class="hljs-keyword">from</span> <span class="hljs-string">'openai'</span>;

<span class="hljs-keyword">const</span> API_KEY = <span class="hljs-string">'YOUR-API-KEY'</span>;
</code></pre>
<p>A <code>useOpenAI</code> function representing the hook was created.</p>
<p>Within the function, an instance of the OpenAI object was created. Also, there’s a <code>getCompletion</code> async function that receives the prompt from the argument and sends the request to the OpenAI API. If there is an error along the way, an error message is returned.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useOpenAI</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> OpenAI({ <span class="hljs-attr">apiKey</span>: API_KEY, <span class="hljs-attr">dangerouslyAllowBrowser</span>: <span class="hljs-literal">true</span> });

  <span class="hljs-keyword">const</span> getCompletion = <span class="hljs-keyword">async</span> (prompt: string) =&gt; {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">let</span> completion = <span class="hljs-keyword">await</span> client.chat.completions.create({
        <span class="hljs-attr">messages</span>: [
            { <span class="hljs-string">"role"</span>: <span class="hljs-string">"system"</span>, <span class="hljs-string">"content"</span>: <span class="hljs-string">"Your job is to write about any topic asked by the user"</span> },
            { <span class="hljs-string">"role"</span>: <span class="hljs-string">"user"</span>, <span class="hljs-string">"content"</span>: prompt }
        ],
        <span class="hljs-attr">model</span>: <span class="hljs-string">"gpt-3.5-turbo"</span>,
        });

      <span class="hljs-keyword">return</span> completion.choices[<span class="hljs-number">0</span>].message;

    } <span class="hljs-keyword">catch</span>(e){
      <span class="hljs-keyword">return</span> {<span class="hljs-string">"role"</span>: <span class="hljs-string">"assistant"</span>, <span class="hljs-string">"content"</span>: <span class="hljs-string">"Something went wrong"</span>}
    }
  };

  <span class="hljs-keyword">return</span> getCompletion;

}
</code></pre>
<p>Remember that you get an error message any time you send a message from the site.</p>
<p>This is because you haven’t added the OpenAI API keys to the project. We'll address that part later.</p>
<h3 id="heading-the-packagejson-file"><strong>The package.json file</strong></h3>
<p><code>Package.json</code> file contains information about the libraries and scripts needed to run your project</p>
<p>The file must look like this:</p>
<pre><code class="lang-javascript">{
  <span class="hljs-string">"name"</span>: <span class="hljs-string">"nextjs-ai"</span>,
  <span class="hljs-string">"version"</span>: <span class="hljs-string">"0.1.0"</span>,
  <span class="hljs-string">"private"</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-string">"dev"</span>: <span class="hljs-string">"next dev"</span>,
    <span class="hljs-string">"build"</span>: <span class="hljs-string">"next build"</span>,
    <span class="hljs-string">"start"</span>: <span class="hljs-string">"next start"</span>,
    <span class="hljs-string">"lint"</span>: <span class="hljs-string">"next lint"</span>
  },
  <span class="hljs-string">"dependencies"</span>: {
    <span class="hljs-string">"@upstash/ratelimit"</span>: <span class="hljs-string">"^2.0.1"</span>,
    <span class="hljs-string">"@vercel/kv"</span>: <span class="hljs-string">"^2.0.0"</span>,
    <span class="hljs-string">"next"</span>: <span class="hljs-string">"14.2.5"</span>,
    <span class="hljs-string">"next-auth"</span>: <span class="hljs-string">"^4.24.7"</span>,
    <span class="hljs-string">"openai"</span>: <span class="hljs-string">"^4.52.7"</span>,
    <span class="hljs-string">"react"</span>: <span class="hljs-string">"^18"</span>,
    <span class="hljs-string">"react-dom"</span>: <span class="hljs-string">"^18"</span>
  },
  <span class="hljs-string">"devDependencies"</span>: {
    <span class="hljs-string">"@types/node"</span>: <span class="hljs-string">"^20"</span>,
    <span class="hljs-string">"@types/react"</span>: <span class="hljs-string">"18"</span>,
    <span class="hljs-string">"@types/react-dom"</span>: <span class="hljs-string">"^18"</span>,
    <span class="hljs-string">"eslint"</span>: <span class="hljs-string">"^8"</span>,
    <span class="hljs-string">"eslint-config-next"</span>: <span class="hljs-string">"14.2.5"</span>,
    <span class="hljs-string">"postcss"</span>: <span class="hljs-string">"^8"</span>,
    <span class="hljs-string">"tailwindcss"</span>: <span class="hljs-string">"^3.4.1"</span>,
    <span class="hljs-string">"typescript"</span>: <span class="hljs-string">"^5"</span>
  }
}
</code></pre>
<p>From the code above, the ‘dev’ script will be used to run the project in development mode. That’s why you use <code>npm run dev</code> in the CLI.</p>
<p>The next 2 scripts, <code>build</code> and <code>start</code>, will be used to optimize the files and start the project in a production environment. You don't need to worry about these since Vercel will sort everything out.</p>
<p><code>next</code>, <code>react</code>, and <code>react-dom</code> are the core libraries for the project.</p>
<p>The <code>openai</code> library will be used to communicate the OpenAI API.</p>
<p><code>@upstash/ratelimit</code>, <code>@vercel/kv</code>, and <code>next-auth</code> will be used to implement some security features in the next sections.</p>
<p>The development dependencies will only be used while developing the project. They include <code>typescript</code>, <code>tailwindcss</code>, and so on.</p>
<h2 id="heading-how-to-get-your-openai-api-keys"><strong>How to Get Your OpenAI API Keys</strong></h2>
<p>To make the project work as intended, you need to add the OpenAI secret key to the project. This key will allow the app to successfully integrate with the OpenAI API, allowing users to send prompt requests from the client side and receive AI responses.</p>
<p>To get your OpenAI secret keys, you first need to sign up (or sign in) on the <a target="_blank" href="https://platforms.openai.com">platforms.openai.com</a> site. Next, you must set up your payment card details if you haven’t. Without them, the API won’t work.</p>
<p>After that, go to the API keys section to create your API key. Make sure you copy it immediately.</p>
<p>Next, you need to store the API key as an environment variable by creating a <code>.env.local</code> file in the root of your project and pasting the value there.</p>
<pre><code class="lang-javascript">NEXT_PUBLIC_API_KEY=<span class="hljs-string">'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'</span>
</code></pre>
<p>The property name starts with <code>NEXT_PUBLIC</code> so it can be used in the client side.</p>
<p>Next, in line 4 of the <code>useOpenAI.ts</code> file, change the code to this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> API_KEY = process.env.NEXT_PUBLIC_API_KEY;
</code></pre>
<p>Once that is done, you can save the files, wait for the Next.js server to recompile, and test the site.</p>
<p>If you’ve turned off the server, navigate to your project’s directory in your CLI and enter <code>npm run dev</code>.  Once the server has compiled, open your browser and type <code>http://localhost:3000/</code> in the search bar. Also, make sure you have your wifi connected so your request can be sent to the OpenAI API.</p>
<p>Once the site loads, try interacting with it, ask some questions, and wait for your reply from the OpenAI servers.</p>
<p>If you followed everything correctly, this is what your interaction should be like:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/5cQWdwxeZpc" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>The styling might not look good but it works. You can work on the styling later.</p>
<p>This is the video for this section:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/kv-oS77w394" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-how-to-deploy-the-project-on-vercel"><strong>How to Deploy the Project on Vercel</strong></h2>
<p>Now it's time to deploy the project on Vercel. We'll use Vercel for deployment in this tutorial, because Vercel creatored Next.js – so the deployment won’t be stressful.</p>
<p>But before that, you'll need to push your changes to GitHub so that you can easily deploy the project to Vercel.</p>
<p>To do that, you need to type the commands below:</p>
<pre><code class="lang-javascript">git init

git add .

git commit –m <span class="hljs-string">"first commit"</span>

git branch -m master main
</code></pre>
<p>Once you've done this, you should go to your GitHub account to create a new repository. Make sure you don’t add any files including a README or License to avoid running into Git errors when deploying.</p>
<p>After creating the repo, copy the link of the repository and paste it into the command below:</p>
<pre><code class="lang-javascript">git remote add origin &lt;link-<span class="hljs-keyword">of</span>-your-repo&gt;
</code></pre>
<p>Now push the code to the main branch:</p>
<pre><code class="lang-javascript">git push -u origin main
</code></pre>
<p>Once you've done this, check out the repo in your GitHub account to ensure it was committed in the main branch.</p>
<p>Once that's done, you need to sign up for Vercel with your GitHub account.</p>
<p>Next, import the nextjs-ai repository from GitHub and deploy it.</p>
<p>Before deploying it, paste the content of your <code>.env.local</code> file to the environment variables section. After that, click <strong>Deploy</strong> and wait for the project to be deployed.</p>
<p>After a few seconds, your app will be deployed, and you’ll be shown the public URL of your project.</p>
<p>When you visit the deployed link, the site should behave like this:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/xDsBqgBTsB0" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>Although the app does what it should, it’s filled with security vulnerabilities that can cost you a lot of money due to cyberattacks. So we'll pause before getting a domain name so we can fix these issues.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/eVai4j47rmM" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-vulnerability-one-exposure-of-sensitive-data-in-the-frontend"><strong>Vulnerability One: Exposure of Sensitive Data in the Frontend</strong></h2>
<p>Exposing sensitive data like API keys in the frontend is dangerous because the data can be stolen and maliciously used by an attacker.</p>
<p>Although you stored the API key as an environment variable, it can still be viewed in the browser. This is because you used the environment variable in a React hook that’ll be executed on the client side.</p>
<p>This means that anyone with enough skills can check your API keys in the browser.</p>
<p>Although the OpenAI API strictly enforces the use of its API on the server side, providing a <code>dangerouslyAllowBrowser</code> prop to remind users of the dangers of using it on the client side, Most APIs don’t have this type of enforcement.</p>
<p>If I was an attacker, here is how I could get your OpenAI API keys:</p>
<p>First, I’d open the browser’s developer tools and click on the network tab. Then, I’d enter a prompt in the input and click enter. As the request was sent, the network tab would capture the outgoing requests and display all the information.</p>
<p>Then when I clicked on the headers tab and navigated to the request headers, I would be able to see the authorization header and the API key.</p>
<p>The video below shows a demonstration:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/ul5kmGYvgDw" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h3 id="heading-how-to-fix-vulnerability-1">How to fix vulnerability 1:</h3>
<p>So how do you fix this? Don't worry – it’s simple.</p>
<p>To prevent exposing data in the client side, you must move the sensitive code to the backend and access your environment variables from the server side. This way, any request from the browser is abstracted and the server as a proxy for communicating with the OpenAI API.</p>
<p>In Next.js, you can do this by using route handlers. We’ll use route handlers to receive incoming requests to the <code>api/ai</code> route, send the prompt to the OpenAI API, and return the responses to the client side.</p>
<p>Route handlers are server-side functions so their code won’t be visible in the browser and your environment variable will be secured.</p>
<p>Now that you know what to do, let’s update the code.</p>
<p>First, remove the <code>NEXT_PUBLIC</code> prefix from <code>NEXT_PUBLIC_API_KEY</code> so it becomes <code>API_KEY</code>. This is to ensure that the key won’t be available on the client side.</p>
<pre><code class="lang-javascript">API_KEY=<span class="hljs-string">'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'</span>
</code></pre>
<p>Next, create an <code>api</code> folder within <code>app</code>. This folder will store all the route handlers for your project.</p>
<p>Next, create an <code>ai</code> folder and a <code>route.ts</code> file within it. The <code>ai</code> folder must be within <code>app/api</code>.</p>
<p>The <code>api/ai/route.ts</code> file will handle requests to the <code>api/ai</code> route.</p>
<p>Next, add this code to your <code>api/ai/route.ts</code> file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> {NextRequest, NextResponse} <span class="hljs-keyword">from</span> <span class="hljs-string">'next/server'</span>;

<span class="hljs-keyword">import</span> OpenAI <span class="hljs-keyword">from</span> <span class="hljs-string">'openai'</span>;

<span class="hljs-keyword">const</span> API_KEY = process.env.API_KEY;

<span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> OpenAI({ <span class="hljs-attr">apiKey</span>: API_KEY });

<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">POST</span> (<span class="hljs-params">req: NextRequest</span>) </span>{
  <span class="hljs-keyword">let</span> {prompt} = <span class="hljs-keyword">await</span> req.json();

  <span class="hljs-keyword">if</span> (!prompt) {
    <span class="hljs-keyword">return</span> NextResponse.json({ <span class="hljs-attr">content</span>: <span class="hljs-string">'Prompt is required'</span> }, {<span class="hljs-attr">status</span>: <span class="hljs-number">400</span>});
  }

  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">let</span> completion = <span class="hljs-keyword">await</span> client.chat.completions.create({
      <span class="hljs-attr">messages</span>: [
          { <span class="hljs-attr">role</span>: <span class="hljs-string">'system'</span>, <span class="hljs-attr">content</span>: <span class="hljs-string">'Your job is to write about any topic asked by the user'</span> },
          { <span class="hljs-attr">role</span>: <span class="hljs-string">'user'</span>, <span class="hljs-attr">content</span>: prompt }
      ],
        <span class="hljs-attr">model</span>: <span class="hljs-string">'gpt-3.5-turbo'</span>,
    });

    <span class="hljs-keyword">return</span> NextResponse.json(completion.choices[<span class="hljs-number">0</span>].message, {<span class="hljs-attr">status</span>: <span class="hljs-number">200</span>});

  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">console</span>.error(error)
    <span class="hljs-keyword">return</span> NextResponse.json({ <span class="hljs-attr">content</span>: <span class="hljs-string">'Internal Server Error'</span> }, {<span class="hljs-attr">status</span>: <span class="hljs-number">500</span>});
  }  
};
</code></pre>
<p>From the code above, you imported the <code>NextRequest</code> and <code>NextResponse</code> functions representing extensions of the Web Request API and Web Response API. You also imported the <code>OpenAI</code> function from <code>openai</code> library.  </p>
<p>Next, you created a variable (<code>API_KEY</code>) that stores the API_KEY environment variable. You also created another variable that stores a new instance of OpenAI object.</p>
<p>Finally, you created a POST function to handle POST requests to the <code>api/ai</code> route. The function receives the prompt and passes it to the OpenAI API, waits for a response, and returns it to the user. If the request doesn’t have a prompt property in the body or there is an error along the way, an error message will be returned to the user.</p>
<p>Next, go to your <code>hooks/useOpenAI.ts</code> file and replace it with these:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> useOpenAI = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> getCompletion = <span class="hljs-keyword">async</span> (prompt: string) =&gt; {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'/api/ai'</span>, {
        <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
        <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({ prompt }),
        <span class="hljs-attr">headers</span>: {
          <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,
        }
      });

      <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> response.json()

      <span class="hljs-keyword">if</span> (!response.ok) {
        <span class="hljs-keyword">return</span> { <span class="hljs-attr">role</span>: <span class="hljs-string">'assistant'</span>, <span class="hljs-attr">content</span>: result.content};
      }

      <span class="hljs-keyword">return</span> result;

    } <span class="hljs-keyword">catch</span> (error) {
      <span class="hljs-keyword">return</span> { <span class="hljs-attr">role</span>: <span class="hljs-string">'assistant'</span>, <span class="hljs-attr">content</span>: <span class="hljs-string">'Something went wrong'</span> };
    }

  };

  <span class="hljs-keyword">return</span> getCompletion;
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> useOpenAI;
</code></pre>
<p>From the code above, you modified the <code>useOpenAI</code> hook so that if <code>getCompletion</code> is called, it will send a fetch request to the <code>api/ai</code> route and return the response to the user. If the request is not successful, an error message will be returned to the user.</p>
<p>If you’ve done that, it’s time to test your endpoint.</p>
<p>In your system, navigate to the project CLI and run the command below:</p>
<pre><code class="lang-javascript">npm run dev
</code></pre>
<p>If you test the site and everything goes well, it means it’s ok.</p>
<p>Now let’s check if you can get the API keys in the Developer tools:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/psjSXJnkJLY" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>From the video above, you can see that no API keys were exposed in the request headers.</p>
<p>Now that this is resolved, you should push the changes to your GitHub repo.</p>
<p>Any changes made in the main branch of your GitHub repo will be automatically deployed to Vercel.</p>
<p>Run these commands to update your GitHub repo:</p>
<pre><code class="lang-javascript">git add .

git commit –m <span class="hljs-string">"moved sensitive code to backend"</span>

git push –u origin main
</code></pre>
<p>After updating the changes, go to your project deployment page on Vercel to confirm that the deployment was successful.</p>
<p>Unfortunately, the deployment is expected to fail because you haven’t updated the environment variables you added to Vercel (from <code>NEXT_PUBLIC_API_KEY</code> to <code>API_KEY</code>).</p>
<p>So you must go to <strong>setting</strong> &gt; <strong>environment variables</strong>, and import the .<code>env.local</code> file of your project.</p>
<p>Once you've done this, go to the deployment page and redeploy the latest change.</p>
<p>After the successful deployment, visit the site and check the network tab to confirm that the API keys are not exposed when you send messages from the prompt.</p>
<p>If no API keys are visible in the request header, that means your sensitive code has been successfully moved to the backend and your latest changes have been deployed.</p>
<p>Congratulations! Now, on to the next part...</p>
<p>But first, here is the video for this section:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/V7Cm6kTyCQE" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-vulnerability-two-dos-and-ddos-attacks"><strong>Vulnerability Two: DOS and DDOS Attacks</strong></h2>
<p>Although our API key is secured in the backend, the app is still vulnerable to denial of service (DOS) and distributed denial of service attacks (DDOS).</p>
<p>A DOS attack is when your site is flooded with excessive requests from a single device that overwhelm your server and prevent your actual users from enjoying the services of your app.</p>
<p>A more advanced one is a distributed denial of service (DDOS) attack which involves sending an overwhelming amount of requests from multiple devices simultaneously to your site.</p>
<p>Different areas of a site can be vulnerable to DOS or DDOS attacks. The attack can target your DNS infrastructure, database, API endpoints, and even your static files.</p>
<p>Without effective mitigation strategies, DoS or DDoS attacks can result in significant financial losses due to skyrocketing cloud service costs and the expenses involved in restoring and securing your site.</p>
<p>To understand how this attack works, let’s try to simulate a simple DOS attack on our AI app.</p>
<p>If you were an attacker, you could execute the script below in your browser’s console to send 50 requests to your <code>api/ai</code> route.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//function to send the request</span>
<span class="hljs-keyword">const</span> getCompletion = <span class="hljs-keyword">async</span> (prompt) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'/api/ai'</span>, {
      <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
      <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({ prompt }),
      <span class="hljs-attr">headers</span>: {
        <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,
      },
   });

   <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> response.json();
   <span class="hljs-keyword">if</span> (!response.ok) {
      <span class="hljs-keyword">return</span> { <span class="hljs-attr">role</span>: <span class="hljs-string">'assistant'</span>, <span class="hljs-attr">content</span>: result.content};
   }

   <span class="hljs-keyword">return</span> result;

  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">return</span> { <span class="hljs-attr">role</span>: <span class="hljs-string">'assistant'</span>, <span class="hljs-attr">content</span>: <span class="hljs-string">'Something went wrong'</span> };
  }
};

<span class="hljs-comment">//function for sending the request 50 times</span>
<span class="hljs-keyword">const</span> attackServer = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> prompt = [<span class="hljs-string">'Write about a lion'</span>, <span class="hljs-string">'write about a tiger'</span>, <span class="hljs-string">'write about america'</span>, <span class="hljs-string">'write about ice cream'</span>, <span class="hljs-string">'write about pizza'</span>];

  <span class="hljs-keyword">const</span> numRequests = <span class="hljs-number">50</span>;

  <span class="hljs-keyword">const</span> results = [];

  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; numRequests; i++) {
    <span class="hljs-keyword">const</span> startTime = performance.now();
    <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> getCompletion( prompt[<span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random()*<span class="hljs-number">4</span>)] );
    <span class="hljs-keyword">const</span> endTime = performance.now();
    <span class="hljs-keyword">const</span> responseTime = endTime - startTime;

    results.push({
      <span class="hljs-attr">index</span>: i,
      result,
      responseTime,
    });

    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Request <span class="hljs-subst">${i + <span class="hljs-number">1</span>}</span>: Response time = <span class="hljs-subst">${responseTime}</span>ms`</span>);
  }

  <span class="hljs-keyword">return</span> results;

};

<span class="hljs-comment">// command to activate the attack and display the result</span>
attackServer().then(<span class="hljs-function">(<span class="hljs-params">results</span>) =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'All requests completed'</span>);
  <span class="hljs-built_in">console</span>.table(results);
});
</code></pre>
<p>From the code above, you pasted two async functions (<code>getCompletion</code> and <code>attackServer</code>) in the console.</p>
<p><code>getCompletion</code> contains the fetch request that’ll be sent to the <code>api/ai</code> route. <code>attackServer</code> contains the code that’ll be used to call the <code>getCompletion</code> function 50 times.</p>
<p>After that, you pasted the last commands that’ll run the <code>attackServer</code> function and display the result containing the information about all the requests sent, including the data received from the server and the response time.</p>
<p>Here is how mine went:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/qh0WgB6Ovy8" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>Although this simple attack involved sending 50 requests to the third-party API route, it actually cost me nearly $0.02 in my OpenAI API usage. If the attack involved 50,000 requests, it’d have cost me $20 dollars. If the attack involved 50,000,000 requests, it’d have cost me nearly $20,000.</p>
<p>Different strategies can be implemented in Next.js and Vercel to protect your site against these attacks. They include rate limiting, firewalls, Vercel/Cloudflare DDOS protection, attack challenge mode, spend management, and website monitoring.</p>
<h3 id="heading-rate-limiting"><strong>Rate limiting</strong></h3>
<p>Rate limiting is a method used for blocking repetitive requests from a device that exceeds a number within a timeframe. If the requests exceed a threshold, the user can be temporarily restricted from accessing a service.</p>
<p>There are different rate-limiting algorithms, but we’ll be using a simple one that restricts access to the api/ai endpoint whenever a user sends too many requests.</p>
<p>We’ll be using Vercel KV database and <code>@upstash/ratelimit</code> library to implement this rate limiting algorithm.</p>
<p><code>@upstash/ratelimit</code> is a powerful rate limiting library designed for use in a serverless environment like Next.js functions. Vercel KV is a Redis database service that we'll use to keep track of the user’s requests.</p>
<p>First, we need to set up Vercel KV. To do that, create a KV database by clicking on the <strong>Storage</strong> \&gt; <strong>Create Vercel KV Database</strong>. Once a dialog box shows up, fill in the necessary information. Give the name field any value, select a region, select your environment (either development, preview, or production), and finally click on connect. Then connect to the project.</p>
<p>Next, check your environment variables in the settings tab to confirm that your KV keys and tokens have been added.</p>
<p>Then create a <code>middleware.ts</code> file in the <code>src</code> folder and add this code to it:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { NextRequest, NextResponse } <span class="hljs-keyword">from</span> <span class="hljs-string">'next/server'</span>;

<span class="hljs-keyword">import</span> { Ratelimit } <span class="hljs-keyword">from</span> <span class="hljs-string">'@upstash/ratelimit'</span>;

<span class="hljs-keyword">import</span> { kv } <span class="hljs-keyword">from</span> <span class="hljs-string">'@vercel/kv'</span>;

<span class="hljs-keyword">const</span> ratelimit = <span class="hljs-keyword">new</span> Ratelimit({
  <span class="hljs-attr">redis</span>: kv,
  <span class="hljs-comment">// 1 requests from the same IP for every 30 seconds</span>
  <span class="hljs-attr">limiter</span>: Ratelimit.slidingWindow(<span class="hljs-number">1</span>, <span class="hljs-string">'30 s'</span>),
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> config = {
  <span class="hljs-attr">matcher</span>: <span class="hljs-string">'/api/ai'</span>
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">middleware</span>(<span class="hljs-params">request: NextRequest</span>) </span>{
  <span class="hljs-keyword">const</span> ip = request.ip || <span class="hljs-string">'127.0.0.1'</span>;
  <span class="hljs-keyword">const</span> { success, pending, limit, reset, remaining } = <span class="hljs-keyword">await</span> ratelimit.limit(
    ip
  );
  <span class="hljs-built_in">console</span>.log(success)
  <span class="hljs-keyword">return</span> success
    ? NextResponse.next()
    : NextResponse.json({ <span class="hljs-attr">role</span>: <span class="hljs-string">'assistant'</span>, <span class="hljs-attr">content</span>: <span class="hljs-string">'too many requests'</span> }, {<span class="hljs-attr">status</span>: <span class="hljs-number">429</span>});
}
</code></pre>
<p>From the code above, you imported <code>NextRequest</code> and <code>NextResponse</code> from <code>next/server</code>, <code>Ratelimit</code> from <code>@upstash/ratelimit</code>, and <code>kv</code> from <code>@Vercel/kv</code>. Next, you set up the <code>Ratelimit</code> function to use KV database and allow only 1 request for every 30 seconds.</p>
<p>Then you created a config variable to ensure that only requests to the <code>api/ai</code> route are rate limited. Finally, you created a middleware function that examines requests to api/ai. If the ip address of the request hasn’t exceeded the rate-limit threshold, it’ll be forwarded to the <code>api/ai</code>. If it has exceeded the threshold, an error message will be returned to the user.</p>
<p>To confirm that the rate-limiting algorithm has been successfully implemented, you'll need to update the GitHub repo and test the latest Vercel deployment.</p>
<p>You can’t test the algorithm in the local server because <code>request.ip</code> is only available on Vercel.</p>
<p>As usual, follow the commands below to push the local changes to GitHub:</p>
<pre><code class="lang-javascript">git add .

git commit –m <span class="hljs-string">"rate limiting algorithm done"</span>

git push –u origin main
</code></pre>
<p>After the update is successful, visit your project deployment page on Vercel to confirm that the GitHub changes have been successfully deployed.</p>
<p>Now visit the link of the deployed site and paste the script below to confirm that your rate-limiting algorithm has been successfully implemented.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//function to send the request</span>
<span class="hljs-keyword">const</span> getCompletion = <span class="hljs-keyword">async</span> (prompt) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'/api/ai'</span>, {
      <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
      <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({ prompt }),
      <span class="hljs-attr">headers</span>: {
        <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,
      },
    });

    <span class="hljs-keyword">if</span> (!response.ok) {
      <span class="hljs-keyword">return</span> { <span class="hljs-attr">role</span>: <span class="hljs-string">'assistant'</span>, <span class="hljs-attr">content</span>: <span class="hljs-string">'Internal server error'</span> };
    }

    <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> response.json();
    <span class="hljs-keyword">return</span> result;

  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">return</span> { <span class="hljs-attr">role</span>: <span class="hljs-string">'assistant'</span>, <span class="hljs-attr">content</span>: <span class="hljs-string">'Something went wrong'</span> };
  }
};

<span class="hljs-comment">//function for sending the request 50 times</span>
<span class="hljs-keyword">const</span> attackServer = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> prompt = [<span class="hljs-string">'Write about a lion'</span>, <span class="hljs-string">'How are you'</span>, <span class="hljs-string">'write about america'</span>, <span class="hljs-string">'write about ice cream'</span>, <span class="hljs-string">'what is your name'</span>];

  <span class="hljs-keyword">const</span> numRequests = <span class="hljs-number">50</span>;

  <span class="hljs-keyword">const</span> results = [];

  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; numRequests; i++) {
    <span class="hljs-keyword">const</span> startTime = performance.now();
    <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> getCompletion( prompt[<span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random()*<span class="hljs-number">4</span>)] );
    <span class="hljs-keyword">const</span> endTime = performance.now();
    <span class="hljs-keyword">const</span> responseTime = endTime - startTime;

    results.push({
      <span class="hljs-attr">index</span>: i,
      result,
      responseTime,
    });

    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Request <span class="hljs-subst">${i + <span class="hljs-number">1</span>}</span>: Response time = <span class="hljs-subst">${responseTime}</span>ms`</span>);
  }

  <span class="hljs-keyword">return</span> results;
};

<span class="hljs-comment">// command to activate the attack and display the result</span>
attackServer().then(<span class="hljs-function">(<span class="hljs-params">results</span>) =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'All requests completed'</span>);
  <span class="hljs-built_in">console</span>.table(results);
});
</code></pre>
<p>Here is how mine went:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/pBkJiNsR7A0" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>From the video above, you can see that most of the responses were error messages. This means the rate-limiting algorithm is working. If any user tries to send more than 1 request every 30 seconds, they’ll get an error message.</p>
<p>Here’s the video for this section:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/iDhAxDEoqHo" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h3 id="heading-vercel-and-cloudflare-ddos-protection"><strong>Vercel and Cloudflare DDOS protection</strong></h3>
<p>In addition to rate limiting, you can use the Vercel automatic DDOS mitigation. According to the Vercel website, there aren’t any charges for the DDOS protection. So all you can do is trust their service.</p>
<p>Also, if you have (or want to purchase) a domain name, you can use Cloudflare and get free, unlimited DDOS protection and security for your app. You can check the <a target="_blank" href="https://www.cloudflare.com/en-gb/ddos">Cloudflare</a> site for more information.</p>
<h3 id="heading-vercel-security-features"><strong>Vercel security features</strong></h3>
<p>In addition to the above measures, you can also use a combination of spend management, attack challenge mode, Vercel WAF rules, and website monitoring to enhance the security of your app.</p>
<p>First, you set up spend management to notify you when your bills reach certain thresholds and pause your projects when they reach an amount. Once you know how much you’ll be spending, you can set the amount in your spend management setting.</p>
<p>Another Vercel security feature is the attack challenge mode. You can use attack challenge mode to make your users pass some verification checks before they can continue using your site. These should be done temporarily when you receive a notification about your bills or notice unusual traffic on your site.</p>
<p>There are also the custom WAF rules. There are different rules in the settings that you can use. You can set rules that restrict access to specific endpoints and request methods. You can also restrict users with a specific request header, ip address, protocol, continent, and country.</p>
<p>It’s always important to monitor your site traffic and your resource usage. If you notice any spike directed at a specific route without the proper usage flow, you can set up attack challenge mode and custom WAF rules to reduce the possibility of an attack.</p>
<p>In summary, to protect your AI app from a DOS/DDOS attack, you can set up rate limiting, spend management, custom firewalls, attack challenge mode, and website monitoring.</p>
<p>I think it’s safe to say this vulnerability has been fixed.</p>
<h2 id="heading-vulnerability-3-no-authentication-and-authorization"><strong>Vulnerability 3: No Authentication and Authorization</strong></h2>
<p>Although you've implemented some measures that protect your site from different forms of attack, another vulnerability can undermine the work you've done so far.</p>
<p>Do you know what it is?</p>
<p>It’s the lack of authentication and authorization mechanisms.</p>
<p>For an app that connects to an external API, the absence of authentication and authorization mechanisms can make your website extremely vulnerable to attacks like DDOS and CSRF. This is because anyone can visit the website and use it without going through any security checks.</p>
<p>Your rate limiting and DDOS security can’t do much if the requests look legitimate and are coming from thousands (or millions) of users. And if all the requests are responded to, it can cost you a lot of money.</p>
<p>Just as I’m able to send prompts to the endpoint, millions of potential users can also do so.</p>
<p>And that is why you need to implement authentication and authorization.</p>
<p>Authentication is when users are verified before they can access a site. Authorization checks if a user is authorized to access or use a feature.</p>
<p>For this job, you’ll be using the <code>next-auth</code> library. Next-auth is a library that allows you to implement different forms of authentication in your Next.js site.</p>
<p>You’ll be using this library to set up GitHub OAuth authentication. This way only those who are authenticated via GitHub will be allowed to send requests to your <code>api/ai</code> route.</p>
<p>First, you must get your Client ID and Client Secret from your GitHub dashboard. Without this, your site won’t be able to use GitHub authentication.</p>
<p>To get this, you must create a GitHub OAuth app (<strong>Settings</strong> &gt; <strong>Developer settings</strong>), and then generate and copy the client id and secret to your <code>.env.local</code> file.</p>
<p>You can check the video below to learn how to do this:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/_TZUYH6hDNI" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>In your <code>.env.local</code> file, the keys should be <code>GITHUB_CLIENT_SECRET</code> and <code>GITHUB_SECRET_ID.</code>  </p>
<pre><code class="lang-javascript">GITHUB_CLIENT_ID=Ov23liRYdIehpA61t3Js
GITHUB_SECRET_ID=<span class="hljs-number">547</span>vfbsjgfsk4859030
</code></pre>
<p>Next, you also need a create a secret key that’ll be used by <code>next-auth</code> to encrypt your JWT tokens. The value must not be easily guessable. You can use the <code>openssl</code> (if you have it installed in your PC) command in your command line so that you can get a complex value that can’t be guessed.</p>
<pre><code class="lang-javascript">openssl rand –base64 <span class="hljs-number">32</span>
</code></pre>
<p>Once that's done, you should copy the value and paste it in your <code>.env.local</code> file</p>
<p>If you don’t have <code>openssl</code>, you can create a complex random value and use it instead.</p>
<pre><code class="lang-javascript">NEXTAUTH_SECRET=OtPuemlSrP8At2uZFIMrc47WBT14pifeKhziIW8
</code></pre>
<p>Next, you need to specify the base path for the authentication. It’s generally the homepage URL of the site. This means the authentication and authorization will encompass all your routes. Since you’ll be testing the site in the local environment, you’ll use <code>http://localhost:3000/</code>.</p>
<p>This means that you must have another key (<code>NEXTAUTH_URL</code>) in your <code>.env.local</code> file.</p>
<pre><code class="lang-javascript">NEXTAUTH_URL=http:<span class="hljs-comment">//localhost:3000/</span>
</code></pre>
<p>Once that's done, you need to create a helper function that’ll store the configurations for <code>next-auth</code>.</p>
<p>Within the app folder, create a <code>helper</code> folder and add an <code>authOption.ts</code> file to it.</p>
<p>Next, add this code to the <code>app/helper/authOptions.ts</code> file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { NextAuthOptions } <span class="hljs-keyword">from</span> <span class="hljs-string">"next-auth"</span>;

<span class="hljs-keyword">import</span> GithubProvider <span class="hljs-keyword">from</span> <span class="hljs-string">"next-auth/providers/github"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> authOptions: NextAuthOptions = {
  <span class="hljs-comment">// Configure one or more authentication providers</span>
  <span class="hljs-attr">providers</span>: [
    GithubProvider({
      <span class="hljs-attr">clientId</span>: process.env.GITHUB_CLIENT_ID <span class="hljs-keyword">as</span> string,
      <span class="hljs-attr">clientSecret</span>: process.env.GITHUB_SECRET <span class="hljs-keyword">as</span> string,
    })
  ],
  <span class="hljs-attr">secret</span>: process.env.NEXTAUTH_SECRET <span class="hljs-keyword">as</span> string,
  <span class="hljs-attr">session</span>: {
    <span class="hljs-attr">strategy</span>: <span class="hljs-string">'jwt'</span>,
    <span class="hljs-attr">maxAge</span>: <span class="hljs-number">60</span> * <span class="hljs-number">2</span> <span class="hljs-comment">//expires 2 minutes after the last request</span>
  },
};
</code></pre>
<p>From the code above, you created the configuration that’ll be used by <code>next-auth</code>. The configuration specifies that the app will be using the GitHub provider for authentication. The secret was also added to the configuration. And finally, you’ll be using a JWT token configured to expire 2 minutes after the last request.</p>
<p>Next, create an <code>api/auth/[…nextauth]/route.ts</code> file in your app folder.</p>
<p>The folder structure should look like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724056167587/f2ab8235-2eca-4d7b-ac92-52ab9574015e.png" alt="Image showing the updated folder structure after creating more folders and files" class="image--center mx-auto" width="482" height="534" loading="lazy"></p>
<p>Next, add the code below to the file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> NextAuth <span class="hljs-keyword">from</span> <span class="hljs-string">"next-auth"</span>;

<span class="hljs-keyword">import</span> {authOptions} <span class="hljs-keyword">from</span> <span class="hljs-string">"@/app/helper/authOption"</span>;

<span class="hljs-keyword">const</span> handler = NextAuth(authOptions);

<span class="hljs-keyword">export</span> { handler <span class="hljs-keyword">as</span> GET, handler <span class="hljs-keyword">as</span> POST };
</code></pre>
<p>From the code above, you imported <code>NextAuth</code> and the <code>authOptions</code> function (from <code>app/helper/authOption</code>). Next, you used this <code>authOption</code> to initialize <code>next-auth</code>. Finally, you exported the handler so that you can use it on the server side.</p>
<p>Next, create a client component that makes the session accessible on the client side. The component will be used to wrap the content of the root layout file (<code>app/layout.tsx</code>).</p>
<p>Within your helper folder, create a <code>provider.tsx</code> file and add this code to it:</p>
<pre><code class="lang-javascript"><span class="hljs-string">"use client"</span>;

<span class="hljs-keyword">import</span> {SessionProvider} <span class="hljs-keyword">from</span> <span class="hljs-string">"next-auth/react"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Provider</span>(<span class="hljs-params">{children}: {children: React.ReactNode}</span>) </span>{
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">SessionProvider</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">SessionProvider</span>&gt;</span></span>;
}
</code></pre>
<p>Next, in your root layout (<code>app/layout.tsx</code>), import the session provider and wrap it around the children prop of the <code>RootLayout</code> component:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//layout.tsx</span>

<span class="hljs-keyword">import</span> {Provider} <span class="hljs-keyword">from</span> <span class="hljs-string">"@/app/helper/provider"</span>;

<span class="hljs-comment">//…other code</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">RootLayout</span>(<span class="hljs-params">{
  children,
}: Readonly&lt;{
  children: React.ReactNode;
}&gt;</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{inter.className}</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Provider</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">Provider</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span></span>
);
}
</code></pre>
<p>Once the session provider has been wrapped around the project, it means you can use the session on the client side to enforce authentication and authorization.</p>
<p>In your <code>app/page.tsx</code> file, you need to import the <code>signIn</code>, <code>signOut</code>, and <code>useSession</code> function from <code>next-auth/react</code>. This will allow the users to be able to sign in, sign out, and view their profile information.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> {signIn, signOut, useSession} <span class="hljs-keyword">from</span> <span class="hljs-string">'next-auth/react'</span>;
</code></pre>
<p>Next, within the <code>Home</code> component, add this code to it:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> {<span class="hljs-attr">data</span>: session, status} = useSession();
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"status"</span>, status);
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"session"</span>, session);
</code></pre>
<p>The code above gets the session and status (authenticated or unauthenticated) of the user from the <code>useSession</code> hook. Then the status and session will be displayed in the browser’s console so that the behavior can be observed. When you’re deploying the changes you can remove the console.log.</p>
<p>Next, replace the code in the header element (from lines 92 to 94) with this:</p>
<pre><code class="lang-javascript">&lt;header className=<span class="hljs-string">"flex flex-row fixed w-[100%] top-0 left-0 p-[10px] px-[20px] text-white text-center bg-[#242424]"</span>&gt;
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-[15px]'</span>&gt;</span>WriterAI<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span></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">"ml-auto flex flex-row gap-[10px]"</span>&gt;</span>
        { session
            ? <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>{session.user.name}<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
            : <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> { signIn('github') }} className="cursor-pointer"&gt;Sign in<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
          }
          { session &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> { signOut() }} className="cursor-pointer"&gt;Sign out<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
&lt;/header&gt;
</code></pre>
<p>From the code above, when the user clicks on the sign-in link, they will be redirected to GitHub to authorize the information transfer. And when the sign-out link gets clicked, the session will be destroyed.</p>
<p>Also, if the page is loaded while the session is still active, the user information and a sign-out link will be displayed. But if there is no session, only a sign-in link will be displayed.</p>
<p>Also, replace the code in the form container (lines 129 to 135) so the form is hidden from unauthenticated users and only those who are authenticated can send prompts to the server.</p>
<pre><code class="lang-javascript">&lt;div className=<span class="hljs-string">'fixed w-[100%] p-[10px] bottom-0 bg-[#242424]'</span>&gt;
     {<span class="hljs-comment">/*when the form is submitted, activate a submit event that sends the value of the input and the event to the handleChat function */</span>}
     {session  
       ? <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">action</span>=<span class="hljs-string">''</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{(e)</span> =&gt;</span> handleChat(input, e)}&gt;
           <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" lg:w-[50%] w-[70%] ml-[5%] lg:ml-[20%] p-[10px] outline-none bg-[#4d4d4dff] text-[15px] text-[#f2f2f2ff]"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'text'</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{input}</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">'Ask your questions'</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{</span> (<span class="hljs-attr">e</span>) =&gt;</span> setInput(e.target.value)}/&gt;
           <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'py-[10px] px-[20px] bg-black text-[15px] text-[#f2f2f2ff]'</span>&gt;</span>Ask<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>
       : <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"block text-white text-[20px] text-center cursor-pointer"</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> { signIn('github') }}&gt;Sign in to send messages<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span></span>
     }
&lt;/div&gt;
</code></pre>
<p>Now if you save the changes and reload your site, this should be how your site looks:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724056553338/6bad6ba5-b16d-4d96-8202-2b6e71f6648f.png" alt="Image showing how the project should look for unauthenticated users" class="image--center mx-auto" width="1919" height="1014" loading="lazy"></p>
<p>When you click on the sign-in link, you’ll be redirected to GitHub for authorization and redirected back to the site after that. Once the authorization is successful, the user information will be visible in the site.</p>
<p>Here is mine:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724056571863/01440fb8-b2d7-4d8a-9c93-d220886361e6.png" alt="Image showing how the project should look for authenticated users" class="image--center mx-auto" width="1064" height="867" loading="lazy"></p>
<p>If you check the browser’s console, you’ll see the information of the session including the name, email, and expiration time/date. Remember that we configured the session to expire 2 minutes after the last request sent to the server. If you don’t send any request to the server within 2 minutes, the session will be destroyed and you’ll be asked to sign in again.</p>
<p>Remember that this is the setting I added in my <code>helper/authOption.ts</code> file. You can configure the session to be active for days, weeks, or months.</p>
<p>There’s no need to worry when you send a prompt and get error messages. This is because you have some environment variables in Vercel that haven’t been added to the <code>.env.local</code> file. When you finally update the changes, you’ll be able to send your prompts to the deployed site as usual.</p>
<p>When you also click on the sign-out link, the session will be destroyed, the app will be reloaded, and the form will be hidden.</p>
<p>If it works as I just explained, that means everything went well.</p>
<p>Also, you need to add authorization in the api/ai route so that unauthorized users won’t be able to send requests directly to the endpoint.</p>
<p>In your <code>api/ai/route.ts</code> file, you need to import <code>getServerSession</code> and <code>authOptions</code>.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> {authOptions} <span class="hljs-keyword">from</span> <span class="hljs-string">"@/app/helper/authOption"</span>;

<span class="hljs-keyword">import</span> { getServerSession} <span class="hljs-keyword">from</span> <span class="hljs-string">"next-auth"</span>;
</code></pre>
<p>Next, within the POST function, add this code to it:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> session = <span class="hljs-keyword">await</span> getServerSession(authOptions);

<span class="hljs-keyword">if</span> (!session) {
  <span class="hljs-keyword">return</span> NextResponse.json({<span class="hljs-attr">content</span>: <span class="hljs-string">'Unauthorized access. Authentication required'</span>}, {<span class="hljs-attr">status</span>: <span class="hljs-number">401</span>})
}
</code></pre>
<p>The code above also prevents unauthorized users from getting responses from the <code>api/ai</code> route by sending a 401 status code and an error message.</p>
<p>Once that's done, it’s time to push the changes to the GitHub repo.</p>
<p>But before that, you need to copy the environment variables (<code>GITHUB_CLIENT_ID</code>, <code>GITHUB_SECRET</code>, <code>NEXTAUTH_SECRET</code>, <code>NEXTAUTH_URL</code>) in your <code>.env.local</code> file and paste into the environment variables section of your project settings page on Vercel.</p>
<p>After that, run the code below to push the changes to GitHub:</p>
<pre><code class="lang-javascript">git add .

git commit –m <span class="hljs-string">"added GitHub authentication and authorization"</span>

git push –u origin main
</code></pre>
<p>After the update is successful, visit your project deployment page on Vercel to confirm that the GitHub changes have been successfully deployed.</p>
<p>But unfortunately, an error we didn’t notice will make the deployment fail.</p>
<p>This is the error message:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724056626098/46178ff9-1f7a-41e6-a056-41fc04e91cc0.png" alt="Image showing the deployment error that'll be seen on Vercel" class="image--center mx-auto" width="1483" height="784" loading="lazy"></p>
<p>Since I also didn’t know what went wrong, I asked ChatGPT for help. And I was able to find the issue.</p>
<p>The deployment failed because we didn't ensure that <code>session.user</code> was defined before accessing it</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724056744333/e512a378-a3d9-4a00-bf21-c425bd1f5848.png" alt="Image showing the messages from ChatGPT" class="image--center mx-auto" width="950" height="627" loading="lazy"></p>
<p>So in your <code>page.tsx</code> file, make sure you fix the code as shown below:</p>
<pre><code class="lang-javascript">{ session &amp;&amp; session.user
    ? <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>{session.user.name}<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span></span>
    : <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> { signIn('github') }} className="cursor-pointer"&gt;Sign in<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span></span>
}
</code></pre>
<p>After that, push the changes to GitHub and wait for the project to be successfully deployed to Vercel.</p>
<p>Now you can visit the deployed link to start testing the authentication.</p>
<p>But when you click on the sign-in link, the page doesn’t load. This is because you haven’t changed the URL in your environment variable and GitHub from <code>http://localhost:3000</code> to the domain of your deployed project.</p>
<p>So you need to check your Project deployment information on Vercel and copy the domain name.</p>
<p>Next, go to your environment variables setting and update the <code>NEXTAUTH_URL</code> to the project’s domain name. Mine is <code>https://nextjs-ai-pro.vercel.app</code>.</p>
<p>Next, go to the OAuth app you created, replace the domain name in the homepage URL and callback URL from <code>http://localhost:3000</code>/ to the Vercel domain name.</p>
<p>Remember that if you have your domain name (for example, domain.com), it’ll be used instead of the Vercel domain name.</p>
<p>If you reload the site and click on the sign-in link, you’ll be redirected to GitHub and your session will be created. When you sign out, the session will also be destroyed.</p>
<p>When you also try executing the script below in your browser’s console, you’ll get an error message because you haven’t signed in.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//function to send the request</span>
<span class="hljs-keyword">const</span> getCompletion = <span class="hljs-keyword">async</span> (prompt) =&gt; {

  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'/api/ai'</span>, {
      <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
      <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({ prompt }),
      <span class="hljs-attr">headers</span>: {
        <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,
      },
  });

  <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> response.json();

  <span class="hljs-keyword">if</span> (!response.ok) {
    <span class="hljs-keyword">return</span> { <span class="hljs-attr">role</span>: <span class="hljs-string">'assistant'</span>, <span class="hljs-attr">content</span>: result.content };
  }

  <span class="hljs-keyword">return</span> result;

  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">return</span> { <span class="hljs-attr">role</span>: <span class="hljs-string">'assistant'</span>, <span class="hljs-attr">content</span>: <span class="hljs-string">'Something went wrong'</span> };
  }
};

getCompletion().then( <span class="hljs-function">(<span class="hljs-params">result</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(result)
})
</code></pre>
<p>This means that unauthorized users won’t be able to run any script in your developer tools to get an AI response.</p>
<p>You can also test the site and send some prompts.</p>
<p>If it all works as explained, that means that authentication and authorization have been successfully implemented. Congratulations!</p>
<p>You can watch the video for this section below:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/YhXuTtHkY3A" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-optimizing-the-code"><strong>Optimizing the Code</strong></h2>
<p>Now most of the work is done. But you can do some other things to further optimize your code. This completely depends on how you want it to be.</p>
<p>For example, you can refactor the code, improve the styling, increase the function timeout for longer prompts, optimize the logs, handle errors in the middleware, set up unit and integration testing, set up a CI/CD pipeline, update your project metadata (title, description, logo), create a preview environment for your deployments, redirect unsuccessful sign-ins to a landing page, and add many more features to your app.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>In this tutorial, we’ve explored a lot of things. You learned how to integrate your Next.js app with third-party APIs. You also learned how to secure your app from the most common cyberattacks by implementing strategies like rate limiting, setting up firewalls, and implementing authentication and authorization on your AI app deployed on Vercel.</p>
<p>By applying the same logic in your Next.js AI app, I’m confident that you’ll be able to deploy your app safely without the fear of waking up to an astronomical bill or server crashes.</p>
<p>To be on the safe side, make sure you monitor your Vercel deployment daily. You can also hire a professional software engineer to verify the security of your app if you don't feel confident in your skills in this area.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build an Online Image-to-PDF Converter with HTML, CSS, JS, and NodeJS ]]>
                </title>
                <description>
                    <![CDATA[ An online image-to-PDF converter is a website that helps you convert your images to PDFs. This tool is useful because it provides an efficient way to store your images. In this tutorial, you'll learn how you can create your online image-to-pdf conver... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-an-online-image-to-pdf-converter-with-html-css-js-nodejs/</link>
                <guid isPermaLink="false">66bb54cc5a83db22bea98422</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gideon Akinsanmi ]]>
                </dc:creator>
                <pubDate>Wed, 30 Aug 2023 17:03:33 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/08/png-img-pdf.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>An online image-to-PDF converter is a website that helps you convert your images to PDFs. This tool is useful because it provides an efficient way to store your images.</p>
<p>In this tutorial, you'll learn how you can create your online image-to-pdf converter with HTML, CSS, JavaScript, and NodeJS.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></li>
<li><a class="post-section-overview" href="#heading-project-setup">Project setup</a></li>
<li><a class="post-section-overview" href="#heading-steps-to-follow">Steps to Follow</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-the-root-url-route">How to Create the Root URL Route</a></li>
<li><a class="post-section-overview" href="#heading-how-to-upload-images-to-the-server">How to Upload Images to the Server</a></li>
<li><a class="post-section-overview" href="#heading-how-to-sort-the-images-and-convert-them-to-pdf">How to Sort the Images and Convert them to PDF</a></li>
<li><a class="post-section-overview" href="#startingover">Starting over</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before starting this project, you should have or know the following languages/libraries/frameworks:</p>
<ul>
<li><p>HTML, CSS, and JavaScript: You must have a basic knowledge of using HTML, CSS, and JavaScript to follow along with this tutorial. You should know how to create these files and link them together. You should know about fundamental HTML elements, core CSS selectors, JavaScript concepts, and the DOM.</p>
</li>
<li><p>NodeJS and npm: You need to have npm and Node.js installed because we'll use them for installing the necessary packages for your project. Specifically, you must have a basic knowledge of how to import and use Nodejs built-in modules.</p>
</li>
<li><p><a target="_blank" href="https://www.npmjs.com/package/nodemon">Nodemon</a>: Nodemon is an important Node package that’ll help you develop your project faster. It helps in restarting your server when you make changes to the project.</p>
</li>
<li><p><a target="_blank" href="https://www.expressjs.com">Express.js</a> and express-generator: Express.js is the Node framework you’ll use to build the web server. Express-generator is a library that'll help you create the necessary files and folders for Express.js to run efficiently. You should know how to create a basic Express application.</p>
</li>
<li><p><a target="_blank" href="https://www.npmjs.com/package/express-session">Express-session</a>: This is an Express middleware library that'll help you manage the application sessions. You should know about the configurations of this library.</p>
</li>
<li><p><a target="_blank" href="https://pugjs.org">Jade/Pug</a>: This is a JavaScript templating engine that'll help you render the address of the uploaded images in an HTML file. You should know the basics of this library.</p>
</li>
<li><p><a target="_blank" href="https://pdfkit.org">PDFkit</a>: This is a JavaScript library that we'll use to convert the images to PDF.</p>
</li>
<li><p><a target="_blank" href="https://www.npmjs.com/package/multer">Multer</a>: This is a Node library that'll handle the file uploads. </p>
</li>
<li><p><a target="_blank" href="https://www.npmjs.com/package/sortablejs">Sortablejs</a>: This is a JavaScript drag-and-drop library that we'll use in the frontend for rearranging our images before they are converted to PDF. </p>
</li>
</ul>
<p>Finally, you must know how to create folders and files (with their appropriate extensions). You should know how to edit these files with a text editor.</p>
<h2 id="heading-project-setup">Project Setup</h2>
<p>First, you need to create a folder and navigate to it with the CLI:</p>
<pre><code>mkdir img2pdf
cd img2pdf
</code></pre><p>Next, initialize it as a npm package so that you’ll be able to install all the necessary libraries you need.</p>
<pre><code>npm init –y
</code></pre><p>If everything goes well, there'll be a package.json file in your folder. </p>
<p>The package.json should look like this:</p>
<pre><code>{
  <span class="hljs-string">"name"</span>: <span class="hljs-string">"img2pdf"</span>,
  <span class="hljs-string">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-string">"description"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-string">"main"</span>: <span class="hljs-string">"index.js"</span>,
  <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-string">"test"</span>: <span class="hljs-string">"echo \"Error: no test specified\" &amp;&amp; exit 1"</span>
  },
  <span class="hljs-string">"keywords"</span>: [],
  <span class="hljs-string">"author"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-string">"license"</span>: <span class="hljs-string">"ISC"</span>
}
</code></pre><p>Next, we'll install express-generator like this:</p>
<pre><code>npx express-generator
npm install
</code></pre><p>We'll choose <code>jade</code> as the default templating engine.</p>
<p>The command above will set up all the necessary folders and libraries needed for Express.js to run efficiently.</p>
<p>At the end of the installation, your project folder will look like this:</p>
<pre><code>/img2pdf
  /bin
    www
  /node_modules
    /public
      /images
      /javascripts
      /stylesheets
  /routes
  /views
  app.js
  package.json
  package-lock.json
</code></pre><ul>
<li><code>bin/www</code> is the entry point of your application.</li>
<li><code>node_modules</code> folder stores the packages needed for our project.</li>
<li><code>public</code> folder will store the static files that'll be returned and stored by the server.</li>
<li><code>routes</code> folder stores all the routes for the application.</li>
<li><code>views</code> folder stores the Jade files that'll be used for server-side rendering.</li>
<li><code>app.js</code> is the root JavaScript file.</li>
</ul>
<p>When you check your <code>package.json</code> file, you'll see that <code>Express.js</code>, <code>Jade</code>, and some other libraries have been installed.</p>
<p>Next, we'll install the Nodemon package globally.</p>
<pre><code>npm install -g nodemon
</code></pre><p>Finally, we'll install <code>PDFkit</code>, <code>Multer</code>, <code>Sortablejs</code>, and <code>Express-session</code>.</p>
<pre><code>npm i multer pdfkit sortablejs express-session
</code></pre><p>Next, add a <code>devstart</code> script in your package.json. It'll allow Nodemon to restart your application when you make any changes to your JavaScript files.</p>
<pre><code><span class="hljs-string">"devstart"</span>: <span class="hljs-string">"nodemon ./bin/www"</span>
</code></pre><p>At the end of your installation, your package.json file should look like this:</p>
<pre><code>{
  <span class="hljs-string">"name"</span>: <span class="hljs-string">"img2pdf"</span>,
  <span class="hljs-string">"version"</span>: <span class="hljs-string">"0.0.0"</span>,
  <span class="hljs-string">"private"</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-string">"start"</span>: <span class="hljs-string">"node ./bin/www"</span>,
    <span class="hljs-string">"devstart"</span>: <span class="hljs-string">"nodemon ./bin/www"</span>
  },
  <span class="hljs-string">"dependencies"</span>: {
    <span class="hljs-string">"cookie-parser"</span>: <span class="hljs-string">"~1.4.4"</span>,
    <span class="hljs-string">"debug"</span>: <span class="hljs-string">"~2.6.9"</span>,
    <span class="hljs-string">"express"</span>: <span class="hljs-string">"~4.16.1"</span>,
    <span class="hljs-string">"express-session"</span>: <span class="hljs-string">"^1.17.3"</span>,
    <span class="hljs-string">"http-errors"</span>: <span class="hljs-string">"~1.6.3"</span>,
    <span class="hljs-string">"jade"</span>: <span class="hljs-string">"~1.11.0"</span>,
    <span class="hljs-string">"morgan"</span>: <span class="hljs-string">"~1.9.1"</span>,
    <span class="hljs-string">"multer"</span>: <span class="hljs-string">"^1.4.5-lts.1"</span>,
    <span class="hljs-string">"pdfkit"</span>: <span class="hljs-string">"^0.31.0"</span>,
    <span class="hljs-string">"sortablejs"</span>: <span class="hljs-string">"^1.15.0"</span>
  }
}
</code></pre><p>Next, you'll navigate to the project folder and start the application server.</p>
<pre><code>cd img2pdf
set DEBUG=img2pdf:* &amp; npm run devstart
</code></pre><p>Once you get a success message, it means the server is already running.</p>
<p>Open your web browser and type <code>http://localhost:3000/</code> in the search bar.</p>
<p>If everything goes well, this should be your result:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/home_express.PNG" alt="home_express" width="600" height="400" loading="lazy"></p>
<h2 id="heading-steps-to-follow">Steps to Follow</h2>
<p>Before writing the code, let's go through an overview of the steps that we'll follow to build this project:</p>
<ol>
<li><p>First you'll define a route that returns the <code>index</code> HTML file when the root URL <code>/</code> is reached.</p>
</li>
<li><p>Within your <code>index</code> HTML file, you'll create a form that accepts only image files (png, jpg) and then send it to the server at a defined route.</p>
</li>
<li><p>When the server receives the images with <code>Multer</code>, it'll store them in a folder, store the address in a session store, and redirect the request to the root URL route which will render a <code>Jade</code> file containing the address of the uploaded images.</p>
</li>
<li><p>Within the <code>Jade</code> file, you'll activate <code>Sortablejs</code> so that the user can rearrange the images before converting to PDF. There'll also be a 'convert to PDF' button which will send the address of the sorted image to the server <code>/pdf</code> route.</p>
</li>
<li><p>When the <code>/pdf</code> route receives the images, you'll use <code>PDFkit</code> to convert the images to PDF. Then you'll send the address of the converted PDF.</p>
</li>
<li><p>When the user clicks on the PDF link, the file will be downloaded to the user's device.</p>
</li>
</ol>
<h2 id="heading-how-to-create-the-root-url-route">How to Create the Root URL Route</h2>
<p>First, we'll be creating a route that sends an index.html file when the root URL (<code>/</code>) is reached.</p>
<p>Here is the simple flowchart of this operation:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/flowchart-homepage.png" alt="Flowchart illustrating the process: Start -> User sends a GET request to the root URL '/' -> Server returns an HTML file -> End" width="600" height="400" loading="lazy"></p>
<p>First, open your routes/index.js file and create a route that returns an HTML file.</p>
<p>Replace all the code in the routes/index.js file with this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">var</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
<span class="hljs-keyword">var</span> router = express.Router();

<span class="hljs-keyword">var</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">'path'</span>);

<span class="hljs-comment">//create a '/' GET route that'll return the index.html file stored in the public/html folder</span>
router.get(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">req, res, next</span>) </span>{
  res.sendFile(path.join(__dirname, <span class="hljs-string">'..'</span>,<span class="hljs-string">'/public/html/index.html'</span>));
}); 

<span class="hljs-built_in">module</span>.exports = router;
</code></pre>
<p>From the code above, we included the <code>express</code> library and activated the <code>express.Router()</code> function. The <code>path</code> module was also included because it'll be used for describing the file paths. </p>
<p>Then we defined a route method that'll handle all the GET requests directed at the root URL <code>/</code>. Anytime the route method receives a request it'll use the <code>res.sendFile()</code> method to send an <code>index.html</code> file back to the user.</p>
<p>The <code>__dirname</code> variable and <code>path.join()</code> method used within <code>res.sendFile(...)</code> method helps us precisely specify the address of the <code>index.html</code> file.</p>
<p>Next, create an <code>html</code> folder within the public folder and add an index.html page to it.</p>
<p>This is how your <code>public</code> folder should look like:</p>
<pre><code>/public
  /html
  /images
  /javascripts
  /stylesheets
</code></pre><p>Next, create an <code>index.html</code> file in your <code>public/html</code> folder and add this code to it:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">DOCTYPE</span> <span class="hljs-attr">HTML</span>&gt;</span> 
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>IMG-to-PDF Converter<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"author"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"YOUR NAME"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"description"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"Easily convert any set of images to PDFs"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Image to PDF converter<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>If your server is still running, go to <code>https://localhost:3000/</code> in your web browser.</p>
<p>This should be the result:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/homepage-1.PNG" alt="homepage-1" width="600" height="400" loading="lazy"></p>
<p>If you haven't started your server, navigate to your project's directory (<code>cd img2pdf</code>) and run this command:</p>
<pre><code>set DEBUG=img2pdf:* &amp; npm run devstart
</code></pre><h2 id="heading-how-to-upload-images-to-the-server">How to Upload Images to the Server</h2>
<p>In this section, I'll be explaining how to upload images to the server. </p>
<p>Here is the simple flowchart for this operation:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/flowchart-file-upload.png" alt="Flowchart illustrating the processs: Start -> User sends images to the server -> Server receives the images, renames them, and stores them in a folder -> Server extracts the image filenames, stores them in a session and redirects the request to the root URL -> The root URL route receives the request and renders a Jade/Pug file containing the image filenames -> end" width="600" height="400" loading="lazy"></p>
<p>Here's what's going on:</p>
<ul>
<li>User sends images to server</li>
<li>Server receives images, renames them, and stores in folder</li>
<li>Server extracts the image filenames, stores them in a session, redirects request to root URL</li>
<li>Root URL route receives the request and renders a Jade/pug file containing the image filenames</li>
<li>End</li>
</ul>
<p>While your server is still running, replace the content in your <code>public/html/index.html</code> file with this:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">DOCTYPE</span> <span class="hljs-attr">HTML</span>&gt;</span> 
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>IMG-to-PDF Converter<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"author"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"Gideon Akinsanmi"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"description"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"Easily convert any set of images to PDFs"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">'stylesheet'</span> <span class="hljs-attr">href</span>=<span class="hljs-string">'/stylesheets/style.css'</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">'stylesheet'</span> <span class="hljs-attr">href</span>=<span class="hljs-string">'/stylesheets/index.css'</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">'/'</span>&gt;</span>IMG2PDF<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">article</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">'title'</span>&gt;</span>Easily convert your PNG and JPG images <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">method</span>=<span class="hljs-string">'post'</span> <span class="hljs-attr">action</span>=<span class="hljs-string">'/upload'</span> <span class="hljs-attr">enctype</span>=<span class="hljs-string">'multipart/form-data'</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">id</span>=<span class="hljs-string">'file-upload'</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'file'</span> <span class="hljs-attr">name</span>=<span class="hljs-string">'images'</span> <span class="hljs-attr">accept</span>=<span class="hljs-string">'.png, .jpg'</span> <span class="hljs-attr">multiple</span>/&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">'file-upload'</span>&gt;</span>Select files<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">id</span>=<span class="hljs-string">'selected-files'</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">code</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">code</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'submit'</span> <span class="hljs-attr">value</span>=<span class="hljs-string">'upload'</span> /&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">article</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">footer</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">code</span>&gt;</span>copyright <span class="hljs-symbol">&amp;copy;</span> IMG2PDF 2023<span class="hljs-tag">&lt;/<span class="hljs-name">code</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
        <span class="hljs-keyword">let</span> fileUpload = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'file-upload'</span>);
        <span class="hljs-keyword">let</span> selectedFiles = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'#selected-files code'</span>);
        <span class="hljs-keyword">let</span> submitButton = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'input[type=submit]'</span>);

        <span class="hljs-keyword">let</span> filenames = <span class="hljs-string">''</span>
        fileUpload.onchange = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>)</span>{
            filenames = <span class="hljs-string">''</span>
            <span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> file <span class="hljs-keyword">of</span> <span class="hljs-built_in">this</span>.files){
                filenames += file.name
                filenames += <span class="hljs-string">','</span>
            }
            selectedFiles.parentElement.style.display = <span class="hljs-string">'block'</span>
            selectedFiles.textContent = filenames
            submitButton.style.display = <span class="hljs-string">'inline-block'</span>;
        }
      </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>In the code above, we linked some CSS files (<code>styles.css</code> and <code>index.css</code>) to the document. We also created a <code>form</code> element that sends a POST request to the <code>/upload</code> route. The <code>form</code> element has an <code>enctype</code> of <code>multipart/form-data</code> (without it, our server can't receive the images).</p>
<p>Within the <code>form</code> element, there's an <code>input</code> element that'll help us get the files from the device. The input has a <code>name</code> attribute with the value of <code>images</code> (which will be used by multer to identify the images). It has been configured to accept multiple image files (with .png or .jpg extensions).</p>
<p>There's also an <code>input</code> element that acts as the submit button. It'll be used to trigger the file upload request.</p>
<p>Then there's the <code>script</code> element that contains some JavaScript code that adds interactivity to the HTML document.</p>
<p>Create a <code>style.css</code> file in your <code>public/stylesheets</code> folder and add this CSS code to it:</p>
<pre><code class="lang-css">* {<span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;<span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;<span class="hljs-attribute">box-sizing</span>: border-box;<span class="hljs-attribute">font-family</span>:Poppins;<span class="hljs-attribute">transition</span>: all <span class="hljs-number">0.5s</span> ease;}
<span class="hljs-selector-tag">main</span> {<span class="hljs-attribute">display</span>: flex;<span class="hljs-attribute">flex-direction</span>:column;<span class="hljs-attribute">height</span>:<span class="hljs-number">100vh</span>;}

<span class="hljs-selector-tag">h1</span> <span class="hljs-selector-tag">a</span> {<span class="hljs-attribute">color</span>: <span class="hljs-number">#ff6600</span>;<span class="hljs-attribute">text-decoration</span>:none;}
<span class="hljs-selector-tag">h1</span> {<span class="hljs-attribute">background-color</span>: white;<span class="hljs-attribute">font-size</span>:<span class="hljs-number">25pt</span>;<span class="hljs-attribute">padding</span>:<span class="hljs-number">5px</span>;<span class="hljs-attribute">text-align</span>: center;}

<span class="hljs-selector-tag">p</span> {<span class="hljs-attribute">font-size</span>:<span class="hljs-number">20pt</span>;<span class="hljs-attribute">text-align</span>: center;<span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">25px</span>;}

<span class="hljs-selector-tag">header</span>, <span class="hljs-selector-tag">footer</span> {<span class="hljs-attribute">border</span>: <span class="hljs-number">2px</span> solid <span class="hljs-number">#ececec</span>;}

<span class="hljs-selector-tag">article</span> {<span class="hljs-attribute">padding</span>:<span class="hljs-number">20px</span>;<span class="hljs-attribute">flex-grow</span>: <span class="hljs-number">1</span>;<span class="hljs-attribute">background-color</span>: <span class="hljs-number">#fff7f0</span>;}
<span class="hljs-selector-tag">footer</span> <span class="hljs-selector-tag">p</span> {<span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">0</span>;<span class="hljs-attribute">font-size</span>: <span class="hljs-number">16pt</span>;}

<span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">380px</span>) {
    <span class="hljs-selector-tag">footer</span> <span class="hljs-selector-tag">p</span> {<span class="hljs-attribute">font-size</span>: <span class="hljs-number">12pt</span>;}
}

<span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">300px</span>) {
    <span class="hljs-selector-tag">h1</span> {<span class="hljs-attribute">font-size</span>: <span class="hljs-number">17pt</span>;}
}
</code></pre>
<p>The CSS code above defines the overall layout of your website.</p>
<p>Next, create an <code>index.css</code> file in your <code>public/stylesheets</code> folder and add this code to it:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">p</span><span class="hljs-selector-class">.title</span> {<span class="hljs-attribute">font-size</span>:<span class="hljs-number">30pt</span>;}
<span class="hljs-selector-tag">p</span><span class="hljs-selector-id">#selected-files</span> {<span class="hljs-attribute">display</span>:none;<span class="hljs-attribute">white-space</span>: nowrap;<span class="hljs-attribute">overflow</span>:hidden;<span class="hljs-attribute">text-overflow</span>: ellipsis;}

<span class="hljs-selector-tag">label</span> {<span class="hljs-attribute">display</span>: inline-block;<span class="hljs-attribute">font-size</span>:<span class="hljs-number">25pt</span>;<span class="hljs-attribute">cursor</span>:pointer;<span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span> <span class="hljs-number">45px</span>;<span class="hljs-attribute">border-radius</span>:<span class="hljs-number">25px</span>;<span class="hljs-attribute">color</span>:white;<span class="hljs-attribute">background-color</span>: <span class="hljs-number">#ff9955</span>;}
<span class="hljs-selector-tag">label</span><span class="hljs-selector-pseudo">:hover</span> {<span class="hljs-attribute">background-color</span>: <span class="hljs-number">#ff6600</span>;}

<span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=file]</span> {<span class="hljs-attribute">display</span>: none;}
<span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=submit]</span> {<span class="hljs-attribute">display</span>: none;<span class="hljs-attribute">font-size</span>:<span class="hljs-number">16pt</span>;<span class="hljs-attribute">cursor</span>:pointer;<span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span> <span class="hljs-number">25px</span>;<span class="hljs-attribute">border-radius</span>:<span class="hljs-number">25px</span>;<span class="hljs-attribute">color</span>:white;<span class="hljs-attribute">background-color</span>:black;}

<span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">380px</span>) {
    <span class="hljs-selector-tag">p</span><span class="hljs-selector-class">.title</span> {<span class="hljs-attribute">font-size</span>: <span class="hljs-number">25pt</span>;}
}

<span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">300px</span>) {
    <span class="hljs-selector-tag">p</span><span class="hljs-selector-class">.title</span> {<span class="hljs-attribute">font-size</span>: <span class="hljs-number">22pt</span>;}
    <span class="hljs-selector-tag">label</span> {<span class="hljs-attribute">font-size</span>: <span class="hljs-number">20pt</span>;}
}

<span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">250px</span>) {
    <span class="hljs-selector-tag">label</span> {<span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span> <span class="hljs-number">35px</span>;}
}
</code></pre>
<p>The CSS code above defines the specific styling of the <code>index.html</code> elements.</p>
<p>Next, you'll open your <code>routes/index.js</code> file and create an <code>/upload</code> route that'll receive the image files, store them in a folder, store the filenames in the session store, and redirect the request to the root URL.</p>
<p>First, we need to edit the <code>app.js</code> file and activate <code>express-session</code>.</p>
<p>Add this code to your <code>app.js</code> file:</p>
<pre><code class="lang-js"><span class="hljs-comment">//include the express-session module</span>
<span class="hljs-keyword">var</span> session = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express-session'</span>);

<span class="hljs-comment">//activate it as an express.js middleware</span>
app.use( session({<span class="hljs-attr">secret</span>: <span class="hljs-string">'YOUR_SECRET'</span>}) )
</code></pre>
<p>This is how your <code>app.js</code> file should look like:</p>
<pre><code class="lang-js"><span class="hljs-keyword">var</span> createError = <span class="hljs-built_in">require</span>(<span class="hljs-string">'http-errors'</span>);
<span class="hljs-keyword">var</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
<span class="hljs-keyword">var</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">'path'</span>);
<span class="hljs-keyword">var</span> logger = <span class="hljs-built_in">require</span>(<span class="hljs-string">'morgan'</span>);
<span class="hljs-keyword">var</span> session = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express-session'</span>);

<span class="hljs-keyword">var</span> indexRouter = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./routes/index'</span>);


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

<span class="hljs-comment">// view engine setup</span>
app.set(<span class="hljs-string">'views'</span>, path.join(__dirname, <span class="hljs-string">'views'</span>));
app.set(<span class="hljs-string">'view engine'</span>, <span class="hljs-string">'jade'</span>);

app.use(logger(<span class="hljs-string">'dev'</span>));
app.use(express.json());
app.use(express.urlencoded({ <span class="hljs-attr">extended</span>: <span class="hljs-literal">false</span> }));
app.use(express.static(path.join(__dirname, <span class="hljs-string">'public'</span>)));
<span class="hljs-comment">//activate express-session</span>
app.use( session({<span class="hljs-attr">secret</span>: <span class="hljs-string">'YOUR_SECRET'</span>}) )

app.use(<span class="hljs-string">'/'</span>, indexRouter);


<span class="hljs-comment">// catch 404 and forward to error handler</span>
app.use(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">req, res, next</span>) </span>{
  next(createError(<span class="hljs-number">404</span>));
});

<span class="hljs-comment">// error handler</span>
app.use(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err, req, res, next</span>) </span>{
  <span class="hljs-comment">// set locals, only providing error in development</span>
  res.locals.message = err.message;
  res.locals.error = req.app.get(<span class="hljs-string">'env'</span>) === <span class="hljs-string">'development'</span> ? err : {};

  <span class="hljs-comment">// render the error page if there's any error occurs</span>
  res.status(err.status || <span class="hljs-number">500</span>);
  res.render(<span class="hljs-string">'error'</span>);
});

<span class="hljs-built_in">module</span>.exports = app;
</code></pre>
<p>Open your <code>routes/index.js</code> file and create a route that'll receive the image filenames, store them in a session and redirect to the root URL:</p>
<pre><code class="lang-js">/<span class="hljs-keyword">import</span> the multer library
<span class="hljs-keyword">var</span> multer = <span class="hljs-built_in">require</span>(<span class="hljs-string">'multer'</span>);
<span class="hljs-keyword">var</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">'path'</span>);

<span class="hljs-comment">//multer file storage configuration</span>
<span class="hljs-keyword">let</span> storage = multer.diskStorage({
    <span class="hljs-comment">//store the images in the public/images folder</span>
    <span class="hljs-attr">destination</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">req, file, cb</span>)</span>{
        cb(<span class="hljs-literal">null</span>, <span class="hljs-string">'public/images'</span>)
    },
    <span class="hljs-comment">//rename the images</span>
    <span class="hljs-attr">filename</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">req, file, cb</span>)</span>{
        cb(<span class="hljs-literal">null</span>, file.fieldname + <span class="hljs-string">'-'</span> + <span class="hljs-built_in">Date</span>.now() + <span class="hljs-string">'.'</span> + file.mimetype.split(<span class="hljs-string">'/'</span>)[<span class="hljs-number">1</span>] )
    }
})

<span class="hljs-comment">//configuration for file filter</span>
<span class="hljs-keyword">let</span> fileFilter = <span class="hljs-function">(<span class="hljs-params">req, file, callback</span>) =&gt;</span> {
    <span class="hljs-keyword">let</span> ext = path.extname(file.originalname);
    <span class="hljs-comment">//if the file extension isn't '.png' or '.jpg' return an error page else return true</span>
    <span class="hljs-keyword">if</span> (ext !== <span class="hljs-string">'.png'</span> &amp;&amp; ext !== <span class="hljs-string">'.jpg'</span>){
        <span class="hljs-keyword">return</span> callback(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Only png and jpg files are accepted'</span>))
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">return</span> callback(<span class="hljs-literal">null</span>, <span class="hljs-literal">true</span>)
    }
}

<span class="hljs-comment">//initialize Multer with the configurations for storage and file filter</span>
<span class="hljs-keyword">var</span> upload = multer({ storage, <span class="hljs-attr">fileFilter</span>: fileFilter});

router.post(<span class="hljs-string">'/upload'</span>, upload.array(<span class="hljs-string">'images'</span>), <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">req, res</span>)</span>{
    <span class="hljs-keyword">let</span> files = req.files;
    <span class="hljs-keyword">let</span> imgNames = [];

    <span class="hljs-comment">//extract the filenames </span>
    <span class="hljs-keyword">for</span>( i <span class="hljs-keyword">of</span> files){
        <span class="hljs-keyword">let</span> index = <span class="hljs-built_in">Object</span>.keys(i).findIndex( <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>)</span>{<span class="hljs-keyword">return</span> e === <span class="hljs-string">'filename'</span>})
        imgNames.push( <span class="hljs-built_in">Object</span>.values(i)[index] )
    }
    <span class="hljs-comment">//store the image filenames in a session</span>
    req.session.imagefiles = imgNames

    <span class="hljs-comment">//redirect the request to the root URL route</span>
    res.redirect(<span class="hljs-string">'/'</span>)
})
</code></pre>
<p>From the code above, we included the <code>Multer</code> library. Then we used the <code>multer.diskStorge()</code> method to describe where the image files will be stored and how they should be renamed. </p>
<p>Also, we created a <code>fileFilter</code> function to ensure that only png and jpg files are stored by the server. If the user sends any file that isn't PNG or JPG, an error page will be displayed with the message "Only png and jpg files are accepted". </p>
<p>Next, we created a route method that listens to POST requests directed at the <code>/upload</code> route. </p>
<p>Within this route method, we included the upload.array('images') method which tells mutler to only store files whose name is <code>images</code> (according to HTML input element <code>&lt;input id='file-upload' type='file' name='images' accept='.png, .jpg' multiple/&gt;</code>). </p>
<p>After that, we extracted the filenames from the <code>req.files</code> property and stored them in the session store. Finally we redirect the request to the root URL route.</p>
<p>In the <code>routes/index.js</code> file, edit the root URL route so that it'll render an <code>index.jade</code> file if the session stores the image filenames.</p>
<pre><code class="lang-js">router.get(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">req, res, next</span>) </span>{
    <span class="hljs-comment">//if there are no image filenames in a session, return the normal HTML page</span>
    <span class="hljs-keyword">if</span> (req.session.imagefiles === <span class="hljs-literal">undefined</span>){
        res.sendFile(path.join(__dirname, <span class="hljs-string">'..'</span>,<span class="hljs-string">'/public/html/index.html'</span>));
    } <span class="hljs-keyword">else</span> {
    <span class="hljs-comment">//if there are image filenames stored in a session, render them in an index.jade file</span>
        res.render(<span class="hljs-string">'index'</span>, {<span class="hljs-attr">images</span>: req.session.imagefiles} )
    }
});
</code></pre>
<p>From the code above, whenn the route URL route receives any request, it first checks if there is an <code>imagefiles</code> property stored in the session store. If there isn't, it sends the <code>index.html</code> file. But if there is, an <code>index.jade</code> file containing the image filenames will be sent.</p>
<p>Finally, go to the <code>views/index.jade</code> file and edit it so that it'll render the uploaded images in HTML:</p>
<pre><code>doctype html
html
  head
    title IMG-to-PDF Converter
    meta(charset=<span class="hljs-string">'UTF-8'</span>)
  body
    h1 Images
    each image <span class="hljs-keyword">in</span> images
      img(src=<span class="hljs-string">`/images/<span class="hljs-subst">${image}</span>`</span> width=<span class="hljs-string">'200'</span> height=<span class="hljs-string">'200'</span>)
</code></pre><p>The code above is written in Jade syntax, when the server renders it, it'll be in form of an HTML document. </p>
<p><strong>Note:</strong> make sure to do the indentation of your content in <code>Jade/Pug</code> with either the space key or tab key, not both.</p>
<p>When you run or restart your server (<code>rs</code> with <code>Nodemon</code>), go to http://localhost:3000/ and restart the upload process (from selecting the files to clicking the upload button).</p>
<p>You should see an HTML page containing the images you uploaded.</p>
<p>Here's my result:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/upload-images.PNG" alt="upload-images" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-sort-the-images-and-convert-them-to-pdf">How to Sort the Images and Convert them to PDF</h2>
<p>In this section, I'll be explaining how you can allow the user to rearrange their images and convert them to a PDF.</p>
<p>Here is the simple flowchart for this operation:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/flowchart-sort-and-upload.png" alt="Flowchart illustrating the process: Start -> User sorts the images -> User clicks on 'convert to PDF' -> Browser sends a request to the server containing the sorted image filenames -> Server receives the request, converts the images to PDF and sends the address back to the browser -> Browser receives the address and displays it to the user  -> User clicks on the link and downloads the PDF -> End" width="600" height="400" loading="lazy"></p>
<p>Here's what's going on:</p>
<ul>
<li>User clicks on "covert to PDF"</li>
<li>Browser sends request to server containing the sorted image filenames</li>
<li>Server receives the request, converts the images to PDF, and sends the address back to the browser</li>
<li>Browser receives the address and displays it to the user</li>
<li>User clicks on the link and downloads the PDF</li>
<li>End</li>
</ul>
<p>In the <code>views/index.jade</code> file, we'll be using <code>Sortablejs</code> to sort the images.</p>
<p>Replace the content in your <code>index.jade</code> file with this:</p>
<pre><code>doctype html
html
  head
    title IMG-to-PDF Converter
    meta(charset=<span class="hljs-string">'UTF-8'</span>)
    link(rel=<span class="hljs-string">'stylesheet'</span> href=<span class="hljs-string">'/stylesheets/style.css'</span>)
    link(rel=<span class="hljs-string">'stylesheet'</span> href=<span class="hljs-string">'/stylesheets/index-view.css'</span>)
    script(type=<span class="hljs-string">'module'</span> src=<span class="hljs-string">'/javascripts/sort.js'</span> defer)
  body
    main
      header
        h1
          a(href=<span class="hljs-string">'/'</span>) IMG2PDF
      article
        p 
          a(href=<span class="hljs-string">'/new'</span>) New +
        div
          each image <span class="hljs-keyword">in</span> images
           img(src=<span class="hljs-string">`/images/<span class="hljs-subst">${image}</span>`</span> data-name=image width=<span class="hljs-string">'200'</span> height=<span class="hljs-string">'200'</span>)
        p
          a(<span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">'convert'</span>)
            span(<span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">'text'</span>) Convert to PDF &amp;rarr;
            span(<span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">'loader'</span>)
        p
          a(<span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">'download'</span> download) Download &amp;darr;
      footer
        p
          code copyright &amp;copy; IMG2PDF <span class="hljs-number">2023</span>
</code></pre><p>The code above is a Jade file that'll be rendered as an HTML document. It contains the document metadata (including the <code>link</code> and <code>style</code> elements that connects to CSS and Javscript files), the image elements, the hyperlinks, and some other HTML elements.</p>
<p>Next, create a <code>public/stylesheets/index-view.css</code> file and add this code to it:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">p</span> <span class="hljs-selector-tag">a</span> {<span class="hljs-attribute">font-size</span>:<span class="hljs-number">20pt</span>;<span class="hljs-attribute">text-decoration</span>: none;<span class="hljs-attribute">color</span>: white;<span class="hljs-attribute">display</span>: inline-block;<span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span> <span class="hljs-number">20px</span>;<span class="hljs-attribute">margin-bottom</span>:<span class="hljs-number">15px</span>;<span class="hljs-attribute">background-color</span>: <span class="hljs-number">#ff6600</span>;<span class="hljs-attribute">cursor</span>: pointer;}

<span class="hljs-selector-tag">div</span> {<span class="hljs-attribute">margin</span>:auto;<span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">15px</span>;<span class="hljs-attribute">padding</span>:<span class="hljs-number">10px</span>;<span class="hljs-attribute">display</span>: flex;<span class="hljs-attribute">flex-direction</span>:row;<span class="hljs-attribute">flex-wrap</span>:wrap;<span class="hljs-attribute">width</span>:<span class="hljs-number">80%</span>;<span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">rgba</span>(<span class="hljs-number">255</span>,<span class="hljs-number">255</span>,<span class="hljs-number">255</span>,<span class="hljs-number">0.9</span>);}

<span class="hljs-selector-tag">div</span> <span class="hljs-selector-tag">img</span> {<span class="hljs-attribute">width</span>: <span class="hljs-number">200px</span>;<span class="hljs-attribute">height</span>:<span class="hljs-number">200px</span>;<span class="hljs-attribute">object-fit</span>: contain;<span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>;<span class="hljs-attribute">background-color</span>: <span class="hljs-number">#ffe6d5</span>;<span class="hljs-attribute">margin-right</span>:<span class="hljs-number">10px</span>;<span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">10px</span>;<span class="hljs-attribute">cursor</span>:pointer;}

<span class="hljs-selector-tag">p</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-class">.download</span> {<span class="hljs-attribute">background-color</span>: black;<span class="hljs-attribute">display</span>:none;}

<span class="hljs-selector-tag">span</span><span class="hljs-selector-class">.loader</span> {<span class="hljs-attribute">display</span>:none;<span class="hljs-attribute">border</span>: <span class="hljs-number">5px</span> solid black;<span class="hljs-attribute">border-top</span>: <span class="hljs-number">5px</span> solid white;<span class="hljs-attribute">border-radius</span>:<span class="hljs-number">50%</span>;<span class="hljs-attribute">width</span>: <span class="hljs-number">25px</span>;<span class="hljs-attribute">height</span>: <span class="hljs-number">25px</span>;<span class="hljs-attribute">animation</span>: spin <span class="hljs-number">0.5s</span> linear infinite;}


<span class="hljs-keyword">@keyframes</span> spin{
    0% {<span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate</span>(<span class="hljs-number">0deg</span>);}
    100% {<span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate</span>(<span class="hljs-number">360deg</span>);}
}

<span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">620px</span>) {    
    <span class="hljs-selector-tag">div</span> <span class="hljs-selector-tag">img</span> {<span class="hljs-attribute">width</span>: <span class="hljs-number">45%</span>;<span class="hljs-attribute">height</span>: <span class="hljs-number">150px</span>;}
}

<span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">415px</span>) {
    <span class="hljs-selector-tag">p</span> <span class="hljs-selector-tag">a</span> {<span class="hljs-attribute">font-size</span>: <span class="hljs-number">16pt</span>;}
    <span class="hljs-selector-tag">div</span> <span class="hljs-selector-tag">img</span> {<span class="hljs-attribute">height</span>: <span class="hljs-number">120px</span>;}
}

<span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">330px</span>) {
    <span class="hljs-selector-tag">p</span> <span class="hljs-selector-tag">a</span> {<span class="hljs-attribute">font-size</span>: <span class="hljs-number">14pt</span>;}
    <span class="hljs-selector-tag">div</span> {<span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;}
    <span class="hljs-selector-tag">div</span> <span class="hljs-selector-tag">img</span> {<span class="hljs-attribute">width</span>:<span class="hljs-number">100%</span>;<span class="hljs-attribute">margin-right</span>: <span class="hljs-number">0px</span>;<span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">5px</span>;}
}

<span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">260px</span>) {
    <span class="hljs-selector-tag">p</span> <span class="hljs-selector-tag">a</span> {<span class="hljs-attribute">font-size</span>: <span class="hljs-number">13pt</span>;<span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span> <span class="hljs-number">10px</span>;}
}
</code></pre>
<p>Next, navigate to the <code>node_modules/sortablejs/modular</code> folder and copy the <code>sortable.core.esm.js</code> file to the <code>public/javascripts</code> folder.</p>
<p>Then create a <code>public/javascripts/sort.js</code> file. </p>
<p>You'll add some code that'll activate <code>Sortablejs</code> and send the sorted filenames to the server for conversion:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> Sortable <span class="hljs-keyword">from</span> <span class="hljs-string">'/javascripts/sortable.core.esm.js'</span>;

<span class="hljs-comment">//use sortablejs on the container element for the image tags</span>
<span class="hljs-keyword">let</span> list = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'div'</span>);
<span class="hljs-keyword">let</span> sort = Sortable.create(list);

<span class="hljs-keyword">let</span> convertButton = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'a.convert'</span>);

<span class="hljs-comment">//When the convert button is clicked</span>
convertButton.onclick = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
    <span class="hljs-keyword">let</span> images = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">'img'</span>);
    <span class="hljs-keyword">let</span> loader = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'span.loader'</span>);
    <span class="hljs-keyword">let</span> convertText = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'span.text'</span>);
    <span class="hljs-keyword">let</span> downloadButton = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'a.download'</span>);

    <span class="hljs-keyword">let</span> filenames = [];
    <span class="hljs-comment">//extract the image names into an array</span>
    <span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> image <span class="hljs-keyword">of</span> images){
        filenames.push(image.dataset.name)
    }
    <span class="hljs-comment">//activate loading animation</span>
    loader.style.display = <span class="hljs-string">'inline-block'</span>;
    convertText.style.display = <span class="hljs-string">'none'</span>

    <span class="hljs-comment">//Create a post request that'll send the image filenames to the '/pdf' route and receive the link to the PDF file</span>
    fetch(<span class="hljs-string">'/pdf'</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(filenames)
    })
    .then( <span class="hljs-function">(<span class="hljs-params">resp</span>)=&gt;</span> {
        <span class="hljs-keyword">return</span> resp.text()
    })
    .then( <span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> {
        <span class="hljs-comment">//stop the loading animation</span>
        loader.style.display = <span class="hljs-string">'none'</span>;

        <span class="hljs-comment">//display the convert and download button</span>
        convertText.style.display = <span class="hljs-string">'inline-block'</span>
        downloadButton.style.display = <span class="hljs-string">'inline-block'</span>

        <span class="hljs-comment">//attach the address to the download button</span>
        downloadButton.href = data
    })
    .catch( <span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> {
        <span class="hljs-built_in">console</span>.error(error.message)
    })    
}
</code></pre>
<p>From the code above, we imported Sortablejs core JavaScript file and activated it on the parent element of the img elements. </p>
<p>When the <code>Convert to PDF</code> button is clicked, we extracted the image filenames from the img elements and sent it as a POST request to the server. </p>
<p>When the browser receives the server response, we attached the link to the download button so that when the user clicks on it, the PDF document will be downloaded on the user's device.</p>
<p>Next, in your <code>routes/index.js</code> file, you'll create a <code>/pdf</code> route that'll receive the sorted filenames and convert them to PDF.</p>
<p>First create a <code>pdf</code> folder in the <code>public</code> folder and add these code to your <code>routes/index.js</code> file:</p>
<pre><code class="lang-js"><span class="hljs-keyword">var</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">'path'</span>);
<span class="hljs-keyword">var</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>);

<span class="hljs-comment">//import PDFkit</span>
<span class="hljs-keyword">var</span> PDFDocument = <span class="hljs-built_in">require</span>(<span class="hljs-string">'pdfkit'</span>);

router.post(<span class="hljs-string">'/pdf'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">req, res, next</span>) </span>{
    <span class="hljs-keyword">let</span> body = req.body

    <span class="hljs-comment">//Create a new pdf</span>
    <span class="hljs-keyword">let</span> doc = <span class="hljs-keyword">new</span> PDFDocument({<span class="hljs-attr">size</span>: <span class="hljs-string">'A4'</span>, <span class="hljs-attr">autoFirstPage</span>: <span class="hljs-literal">false</span>}); 
    <span class="hljs-keyword">let</span> pdfName = <span class="hljs-string">'pdf-'</span> + <span class="hljs-built_in">Date</span>.now() + <span class="hljs-string">'.pdf'</span>;

    <span class="hljs-comment">//store the pdf in the public/pdf folder</span>
    doc.pipe( fs.createWriteStream( path.join(__dirname, <span class="hljs-string">'..'</span>,<span class="hljs-string">`/public/pdf/<span class="hljs-subst">${pdfName}</span>`</span> ) ) );

    <span class="hljs-comment">//create the pdf pages and add the images</span>
    <span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> name <span class="hljs-keyword">of</span> body){
        doc.addPage()
        doc.image(path.join(__dirname, <span class="hljs-string">'..'</span>,<span class="hljs-string">`/public/images/<span class="hljs-subst">${name}</span>`</span>),<span class="hljs-number">20</span>, <span class="hljs-number">20</span>, {<span class="hljs-attr">width</span>: <span class="hljs-number">555.28</span>, <span class="hljs-attr">align</span>: <span class="hljs-string">'center'</span>, <span class="hljs-attr">valign</span>: <span class="hljs-string">'center'</span>} )
    }
    <span class="hljs-comment">//end the process</span>
    doc.end();

    <span class="hljs-comment">//send the address back to the browser</span>
    res.send(<span class="hljs-string">`/pdf/<span class="hljs-subst">${pdfName}</span>`</span>)
})
</code></pre>
<p>From the code above, we included <code>pdfkit</code> and created a route method that responds to POST requests directed at the <code>/pdf</code> URL. Within this route method, we used <code>pdfkit</code> to convert all the images to PDF. Finally, we sent the address of the PDF document back to the document.</p>
<p>When you restart the server, navigate to <code>http://localhost:3000/</code> in your web browser, and restart the file upload process. You'll able to convert your images to PDF.</p>
<p>Here's a YouTube video describing the entire process:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/JRSnpkGXQcA" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-how-to-cancel-and-restart-image-uploads">How to Cancel and Restart Image Uploads</h2>
<p>In this section, we'll be creating a functionality that allows the user to cancel the existing file upload project and create a new one.</p>
<p>Remember that there's a <code>New +</code> button in our <code>views/index.jade</code> that points to the <code>/new</code> route. </p>
<pre><code>...
article
  p 
    a(href=<span class="hljs-string">'/new'</span>) New +
...
</code></pre><p>Now we'll be writing the route in our <code>routes/index.js</code> file.</p>
<p>Here is the simple flowchart for this operation:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/08/flowchart-start-over.png" alt="Flowchart illustrating the process: Start -> User clicks on the 'New +' button -> Browser sends a GET request to the server -> Server deletes the images and clears their filenames from the session store -> Server redirects the request to the root URL -> End" width="600" height="400" loading="lazy"></p>
<p>Here's what's going on:</p>
<ul>
<li>User clicks on the 'New +' button</li>
<li>Browser sends a GET request to the server</li>
<li>Server deletes the images and clears their filenames from the session store</li>
<li>Server redirects the request to the root URL</li>
<li>End</li>
</ul>
<p>Add this code to your <code>routes/index.js</code> file:</p>
<pre><code class="lang-js">router.get(<span class="hljs-string">'/new'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">req, res, next</span>) </span>{
    <span class="hljs-comment">//delete the files stored in the session</span>
    <span class="hljs-keyword">let</span> filenames = req.session.imagefiles;

    <span class="hljs-keyword">let</span> deleteFiles = <span class="hljs-keyword">async</span> (paths) =&gt; {
        <span class="hljs-keyword">let</span> deleting = paths.map( <span class="hljs-function">(<span class="hljs-params">file</span>) =&gt;</span> unlink(path.join(__dirname, <span class="hljs-string">'..'</span>, <span class="hljs-string">`/public/images/<span class="hljs-subst">${file}</span>`</span>) ) )
        <span class="hljs-keyword">await</span> <span class="hljs-built_in">Promise</span>.all(deleting)
    }
    deleteFiles(filenames)

    <span class="hljs-comment">//remove the data from the session</span>
    req.session.imagefiles = <span class="hljs-literal">undefined</span>

    <span class="hljs-comment">//redirect to the root URL</span>
    res.redirect(<span class="hljs-string">'/'</span>)
})
</code></pre>
<p>From the code above, when the <code>/new</code> route receives a GET request, it'll delete the images stored in folder and session store. After that, it'll redirect the request to the root URL.</p>
<p>When you navigate to <code>http://localhost:3000/</code>, upload some images, and cancel the existing file upload process, you'll see that the images were deleted from the folder and you were redirected to the homepage.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>With the code above, you've been able to create an online Image-to-PDF converter. Congrats!</p>
<p>As you know, software development is a continuous process. So you can try implementing some additional functionalities (such as security, input validation, and so on) to the project.</p>
<p>You can check out my <a target="_blank" href="https://github.com/Gidthecoder/img2pdf/">GitHub repo</a> for the complete source code.</p>
<p>If you have any questions for me, you can check my freeCodeCamp profile for my contact details. I'll reply as quick as possible!</p>
<p>Bye for now.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a JavaScript Utility Library like Lodash ]]>
                </title>
                <description>
                    <![CDATA[ A utility library is a library that helps you streamline the implementation of common coding tasks. With it, you only need to focus on writing code that makes your project unique. One of the most popular JavaScript utility libraries is Lodash. Lodash... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-a-javascript-utility-library-like-lodash/</link>
                <guid isPermaLink="false">66bb54d024dd38751ddeb5af</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gideon Akinsanmi ]]>
                </dc:creator>
                <pubDate>Tue, 08 Aug 2023 21:15:08 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/08/inaki-del-olmo-NIJuEQw0RKg-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>A utility library is a library that helps you streamline the implementation of common coding tasks. With it, you only need to focus on writing code that makes your project unique.</p>
<p>One of the most popular JavaScript utility libraries is Lodash. Lodash is a JS library with over 40 million weekly downloads on npm. It helps provide additional functionalities to preexisting vanilla JS objects. Lodash uses a functional programming approach in implementing common tasks in JavaScript.</p>
<p>Although this library is great, using it in smaller projects might be overkill, especially if you only need 3 or 4 functionalities. With a package size of over <a target="_blank" href="https://www.npmjs.com/package/lodash">1.4MB</a> comprising over 1000 files and 500 functions, having Lodash on your front-end can hinder the performance of your website.</p>
<p>In this article, I'll be showing you how to implement some of the key functionalities provided by Lodash. At the end of this tutorial, you’ll not only know how to implement the functionality of a popular library but you’ll also see an improvement in your JavaScript skills.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>For this article, all you need is a code editor, a web browser, and a basic knowledge of JavaScript.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><a class="post-section-overview" href="#heading-project-setup">Project Setup</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-array-methods">How to Create Array Methods</a><ol>
<li><a class="post-section-overview" href="#heading-the-chunk-method">The _.chunk() method</a></li>
<li><a class="post-section-overview" href="#heading-the-compact-method">The _.compact() method</a></li>
<li><a class="post-section-overview" href="#heading-the-concat-method">The _.concat() method</a></li>
<li><a class="post-section-overview" href="#heading-the-drop-method">The _.drop() method</a></li>
<li><a target="_blank" href="the_droprightmethod">The _.dropRight() method</a></li>
<li><a class="post-section-overview" href="#heading-the-fill-method">The _.fill() method</a></li>
<li><a class="post-section-overview" href="#heading-the-flatten-method">The _.flatten() method</a></li>
<li><a class="post-section-overview" href="#heading-the-intersection-method">The _.intersection() method</a></li>
<li><a class="post-section-overview" href="#the_removemethod">The _.remove() method</a></li>
<li><a class="post-section-overview" href="#heading-the-union-method">The _.union() method</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-how-to-create-collection-methods">How to Create Collection Methods</a><ol>
<li><a class="post-section-overview" href="#heading-the-filter-method">The _.filter() method</a></li>
<li><a class="post-section-overview" href="#heading-the-find-method">The _.find() method</a></li>
<li><a class="post-section-overview" href="#heading-the-partition-method">The _.partition() method</a></li>
<li><a class="post-section-overview" href="#heading-the-shuffle-method">The _.shuffle() method</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-how-to-create-math-methods">How to Create Math Methods</a><ol>
<li><a class="post-section-overview" href="#heading-the-mean-method">The _.mean() method</a></li>
<li><a class="post-section-overview" href="#heading-the-max-method">The _.max() method</a></li>
<li><a class="post-section-overview" href="#heading-the-min-method">The _.min() method</a></li>
<li><a class="post-section-overview" href="#heading-the-sum-method">The _.sum() method</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-how-to-create-object-methods">How to Create Object Methods</a><ol>
<li><a class="post-section-overview" href="#heading-the-keys-method">The _.keys() method</a></li>
<li><a class="post-section-overview" href="#heading-the-values-method">The _.values() method</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-how-to-create-string-methods">How to Create String Methods</a><ol>
<li><a class="post-section-overview" href="#heading-the-repeat-method">The _.repeat() method</a></li>
<li><a class="post-section-overview" href="#heading-the-split-method">The _.split() method</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ol>
<h2 id="heading-project-setup">Project Setup</h2>
<p>The first thing to do is to create a folder called <code>lodash-project</code> that contains an HTML file and a JavaScript file. </p>
<p>The JavaScript file will contain the code, while our HTML file will be used to link the files together. </p>
<p>You can give them any valid name you like but I’ll be sticking with <code>index.html</code> and <code>index.js</code>.</p>
<p>Within the HTML file, you’ll add the script element to link with your index.js file.</p>
<p>Here’s the code for <code>index.html</code>:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">HTML</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
             <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Lodash project<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
             <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">'index.js'</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Within the JavaScript file (index.js), we'll be using a JavaScript class (<code>_</code>) and static methods (whose name will be the Lodash methods we'll be implementing) for structuring our code.</p>
<p>Here's the format it should be in:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_</span> </span>{
    <span class="hljs-keyword">static</span> name_of_method(){
        <span class="hljs-comment">//code</span>
    }
}
</code></pre>
<p>From this point onward, I’ll be showing you how to implement 10 Lodash array methods, four collection methods, four math methods, two object methods, and two string methods. Are you ready? Let’s go!</p>
<h2 id="heading-how-to-create-array-methods">How to Create Array Methods</h2>
<h3 id="heading-the-chunk-method">The <code>_.chunk()</code> method</h3>
<p>First, we'll see how to implement the <code>_.chunk()</code> method. In Lodash, the chunk method helps you divide the content of your array into groups. </p>
<p>This method is useful when you want to display a long list of items in a paginated user interface. </p>
<p>By applying the <code>chunk()</code> method, you can divide the array into smaller arrays, each containing a fixed number of items. This allows you to efficiently manage the display of items page by page, enhancing the user experience and minimizing load time.</p>
<p>It can also be used for dealing with data that needs to be processed in parallel or batched operations.</p>
<p>According to the <a target="_blank" href="https://lodash.com/docs/#chunk">Lodash documentation</a>, this is how it works: </p>
<pre><code class="lang-javascript"><span class="hljs-comment">//divides the array into a group of 2</span>
_.chunk([<span class="hljs-string">'a'</span>, <span class="hljs-string">'b'</span>, <span class="hljs-string">'c'</span>, <span class="hljs-string">'d'</span>], <span class="hljs-number">2</span>);
<span class="hljs-comment">//returns [ ['a', 'b'], ['c', 'd'] ]</span>

<span class="hljs-comment">//divides the array into a group of 3 and shifts the rest into another</span>
_.chunk([<span class="hljs-string">'a'</span>, <span class="hljs-string">'b'</span>, <span class="hljs-string">'c'</span>, <span class="hljs-string">'d'</span>], <span class="hljs-number">3</span>);
<span class="hljs-comment">//returns [ ['a', 'b', 'c'], ['d'] ]</span>
</code></pre>
<p>Here's how to implement it yourself: </p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> </span>{
     <span class="hljs-comment">//... other methods</span>

      <span class="hljs-keyword">static</span> chunk(array, size=<span class="hljs-number">1</span>){
             <span class="hljs-keyword">let</span> newArray = [];  
             <span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; array.length; i += size){ 
                    <span class="hljs-keyword">let</span> chunk = array.slice(i, i + size);
                    newArray.push(chunk) 
             } 
             <span class="hljs-keyword">return</span> newArray;                        
      }
}
</code></pre>
<p>In the code above, the first step is creating a static <code>chunk()</code> method with an <code>array</code> and <code>size</code> parameter. The <code>size</code> parameter gets a default value of 1. </p>
<p>Next, we create an empty array (<code>newArray</code>) that’ll store the split chunks. Then we’ll use loops and the <code>array.slice()</code> method to slice out chunks from the original array and push it to the new array (<code>newArray</code>). </p>
<h3 id="heading-the-compact-method">The <code>_.compact()</code> method</h3>
<p>Now let's look at the <code>_.compact()</code> method. In Lodash, the <code>compact()</code> method returns an array with all falsy elements removed. Examples of falsy values are <code>undefined</code>, <code>null</code>, <code>''</code>, <code>false</code>, <code>0</code>, and so on.</p>
<p>This method is useful when you want to clean up an array and focus on meaningful and non-empty values.</p>
<p>You can also use it for cleaning arrays that have incomplete data or irrelevant information.</p>
<p>According to the <a target="_blank" href="https://lodash.com/docs/#compact">Lodash documentation</a>, this is how it works:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> surveyReport = [<span class="hljs-string">'yes'</span>, <span class="hljs-string">'no'</span>, <span class="hljs-string">'not sure'</span>, <span class="hljs-literal">null</span>, <span class="hljs-string">'no'</span>, <span class="hljs-string">''</span>];

<span class="hljs-comment">//removes the falsy elements</span>
_.compact(surveyReport);
<span class="hljs-comment">//returns ['yes', 'no', 'not sure', 'no']</span>
</code></pre>
<p>Here’s how you to implement it yourself:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_</span> </span>{
      <span class="hljs-comment">//... other codes</span>
      <span class="hljs-keyword">static</span> compact(array){
             <span class="hljs-keyword">let</span> newArray = array.filter( <span class="hljs-function"><span class="hljs-params">val</span> =&gt;</span> {<span class="hljs-keyword">return</span> <span class="hljs-built_in">Boolean</span>(val) === <span class="hljs-literal">true</span>})
             <span class="hljs-keyword">return</span> newArray;
      }
}
</code></pre>
<p>From the code above, we created a static <code>compact()</code> method with an <code>array</code> parameter. Then we used the <code>array.filter()</code> method and the <code>Boolean()</code> function to filter out values that are truthy, not falsy. </p>
<h3 id="heading-the-concat-method">The <code>_.concat()</code> method</h3>
<p>In Lodash, the <code>concat()</code> method is used for concatenating two or more arrays or values. This is particularly useful when you're dealing with data from different sources and want to present them in a comprehensive view.</p>
<p>Imagine a scenario where you have multiple arrays containing related information, such as customer names, addresses, and order details. The <code>concat()</code> method allows you to effortlessly combine these arrays, creating a unified dataset that can be easily processed or displayed.</p>
<p>According to the <a target="_blank" href="https://lodash.com/docs/#concat">Lodash documentation</a>, this is how it works:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> array = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>];
<span class="hljs-keyword">let</span> other = _.concat(array, <span class="hljs-number">4</span>, [<span class="hljs-number">1</span>], [ [<span class="hljs-number">12</span>, <span class="hljs-literal">true</span>] ] );

<span class="hljs-built_in">console</span>.log(other)
<span class="hljs-comment">//returns [ 1, 2, 4, 1, [12, true] ]</span>
</code></pre>
<p>Here’s how to implement it yourself:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_</span> </span>{
      <span class="hljs-comment">//...other methods</span>

      <span class="hljs-keyword">static</span> concat(array, ...values){

             <span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; values.length; i++){
                    array = array.concat( values[i] ) 
             } 
             <span class="hljs-keyword">return</span> array;
      }
}
</code></pre>
<p>In the code above, I created a static <code>concat()</code> method with an <code>array</code> and <code>values</code> parameter. The parameter will be treated as an array representing the separate arrays/values we’ll be concatenating. </p>
<p>Next, we used the for loop and the <code>array.concat()</code> method to concatenate every element in the values parameter to our array parameter.</p>
<h3 id="heading-the-drop-method">The <code>_.drop()</code> method</h3>
<p>The <code>drop()</code> method returns an array with some of its elements dropped from the beginning.</p>
<p>The <code>drop()</code> method plays a crucial role in implementing stack-like behavior with arrays. You can use this method to ensure that only the most recent tasks are prioritized.</p>
<p>According to the <a target="_blank" href="https://lodash.com/docs/#drop">Lodash documentation</a>, this is how it works:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//drops the first element and returns the rest</span>
_.drop([<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>])
<span class="hljs-comment">//=&gt; [2,3]</span>

<span class="hljs-comment">//drops the first 2 elements and returns the rest</span>
_.drop([<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">4</span>], <span class="hljs-number">2</span>)
<span class="hljs-comment">//=&gt; [3,4]</span>

<span class="hljs-comment">//drops the first 3 elements and returns the rest</span>
_.drop([<span class="hljs-number">2</span>,<span class="hljs-number">4</span>,<span class="hljs-number">6</span>], <span class="hljs-number">3</span>)
<span class="hljs-comment">//=&gt; []</span>
</code></pre>
<p>Here's how to implement it yourself:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_</span> </span>{
    <span class="hljs-comment">//other methods</span>

    <span class="hljs-keyword">static</span> drop(array, n=<span class="hljs-number">1</span>){
           <span class="hljs-keyword">return</span> array.slice(n, array.length) 
     }
}
</code></pre>
<p>In the code above, we created a static <code>drop()</code> method with an <code>array</code> and <code>n</code> parameter. <code>array</code> represents the array whose elements will be dropped while <code>n</code> represents the number of elements to be dropped from the beginning. </p>
<p>Then we used an <code>array.slice()</code> method to return an array starting from position <code>n</code> to the last element.</p>
<h3 id="heading-the-dropright-method">The <code>_.dropRight()</code> method</h3>
<p>The <code>dropRight()</code> method returns an array with some of its elements dropped from the end.</p>
<p>The <code>drop()</code> method plays a crucial role in implementing queue-like behavior with arrays. You can use this method to ensure that only the oldest tasks are prioritized.</p>
<p>According to <a target="_blank" href="https://lodash.com/docs/#dropRight">Lodash</a>, this is how it works:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//drops the last element and returns the rest</span>
_.dropRight([<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">4</span>]) 

<span class="hljs-comment">//drops the last 2 elements and returns the rest</span>
_.dropRight([<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">4</span>], <span class="hljs-number">2</span>)
<span class="hljs-comment">//[2, 3]</span>
</code></pre>
<p>Here’s how to implement it yourself:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_</span> </span>{
    <span class="hljs-comment">//other methods ...</span>

     <span class="hljs-keyword">static</span> dropRight(array, n=<span class="hljs-number">1</span>){
           <span class="hljs-keyword">return</span> array.slice(<span class="hljs-number">0</span>, -n)
     }
}
</code></pre>
<p>From the code above, we created a static <code>dropRight()</code> method with an <code>array</code> and <code>n</code> parameter. <code>array</code> represents the array whose elements will be dropped while <code>n</code> represents the number of elements to be dropped from the end. </p>
<p>Next, we use the <code>array.slice()</code> method to return an array starting from position 0 to (but not including) –<code>n</code>.</p>
<h3 id="heading-the-fill-method">The <code>_.fill()</code> method</h3>
<p>The <code>fill()</code> method fills an array with a specific value.</p>
<p>Imagine you have an array representing a game board where certain cells need to be marked as occupied. By using the <code>fill()</code> method, you can efficiently replace a range of cells with a marker value, indicating their status.</p>
<p>According to <a target="_blank" href="https://lodash.com/docs/#fill">Lodash</a>, this is how it works:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> board = <span class="hljs-built_in">Array</span>(<span class="hljs-number">9</span>);

<span class="hljs-comment">//replaces or fills all the elements in the array with '0'</span>
_.fill(array, <span class="hljs-number">0</span>);
<span class="hljs-built_in">console</span>.log(array)
<span class="hljs-comment">//returns [0, 0, 0, 0, 0, 0, 0, 0, 0];</span>

<span class="hljs-keyword">let</span> array = [<span class="hljs-number">1</span>,<span class="hljs-number">3</span>,<span class="hljs-number">5</span>,<span class="hljs-number">7</span>];
<span class="hljs-comment">//replaces or fills the array with 'hello' from index 0 to(but not including) index 3</span>
_.fill(array, <span class="hljs-string">'hello'</span>, <span class="hljs-number">0</span>, <span class="hljs-number">3</span>)

<span class="hljs-built_in">console</span>.log(array)
<span class="hljs-comment">//returns ['hello', 'hello', 'hello', 7];</span>
</code></pre>
<p>Here's how to implement it yourself:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_</span> </span>{
    <span class="hljs-comment">//other methods ...</span>

     <span class="hljs-keyword">static</span> fill(array, value, start=<span class="hljs-number">0</span>, end=array.length){ 
           <span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> i = start; i &lt; end; i++){ 
                 array[i] = value 
           } 
           <span class="hljs-keyword">return</span> array;    
     }
}
</code></pre>
<p>In the code above, we created a static <code>fill()</code> method with an <code>array</code>, <code>value</code>, <code>start</code>, and <code>end</code> parameter. </p>
<p><code>array</code> represents the array whose element will be filled. <code>value</code> is the value that’ll fill/replace the elements in the array. <code>start</code> is the position to start filling from. It has a default value of 0. <code>end</code> represents the position that the filling will end at. Its default value is <code>array.length</code>. </p>
<p>Within the method, we created a loop that changes the value of each element to <code>value</code>.</p>
<h3 id="heading-the-flatten-method">The <code>_.flatten()</code> method</h3>
<p>The <code>flatten()</code> method flattens an array one level deep.</p>
<p>Imagine you have an array containing sub-arrays representing different categories of items. By applying the <code>flatten()</code> method, you can seamlessly merge these sub-arrays into a single array, simplifying the process of iterating, searching, or performing operations on the combined dataset. </p>
<p>According to the <a target="_blank" href="https://lodash.com/docs/#flatten">Lodash documentation</a>, this is how it works:</p>
<pre><code class="lang-javascript">_.flatten([[<span class="hljs-string">'James'</span>], [<span class="hljs-number">17</span>], [<span class="hljs-string">'Male'</span>]]);
<span class="hljs-comment">//-&gt; ['James', 17, 'Male']</span>
</code></pre>
<p>Here’s how to implement it:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_</span> </span>{
    <span class="hljs-comment">//... other methods</span>

     <span class="hljs-keyword">static</span> flatten(array){
           <span class="hljs-keyword">return</span> [].concat(...array);
     }
}
</code></pre>
<p>In the code above, we created a static <code>flatten()</code> method with an <code>array</code> argument. Then we used the spread operator and <code>array.concat()</code> to concatenate an empty array with the expanded iterable.</p>
<h3 id="heading-the-intersection-method">The <code>_.intersection()</code> method</h3>
<p>The <code>intersection()</code> method returns an array that contains values that are present in all given arrays.</p>
<p>Imagine you have multiple arrays containing user preferences for different features of your application. By using the <code>intersection()</code> method, you can easily determine which preferences are common across all users, helping you identify the most popular or preferred features. </p>
<p>According to the <a target="_blank" href="https://lodash.com/docs/#intersection">Lodash documentation</a>, this is how it works:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> preference1 = [<span class="hljs-string">'Post'</span>, <span class="hljs-string">'View'</span>, <span class="hljs-string">'Comment'</span>];
<span class="hljs-keyword">let</span> preference2 = [<span class="hljs-string">'Like'</span>, <span class="hljs-string">'Comment'</span>, <span class="hljs-string">'Share'</span>];

_.intersection(preference1, preference2)
returns [<span class="hljs-string">'Comment'</span>]
</code></pre>
<p>Here’s how to implement it yourself:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_</span> </span>{
     <span class="hljs-comment">//...other methods</span>

     <span class="hljs-keyword">static</span> intersection(...arrays){ 
           <span class="hljs-keyword">if</span> (arrays.length === <span class="hljs-number">0</span>){
                 <span class="hljs-keyword">return</span> []
           }
           <span class="hljs-keyword">let</span> intersection = arrays.reduce(<span class="hljs-function">(<span class="hljs-params">prev, current</span>) =&gt;</span> {
                 <span class="hljs-keyword">return</span> prev.filter(<span class="hljs-function">(<span class="hljs-params">element</span>) =&gt;</span> current.includes(element) );
           })

           <span class="hljs-keyword">return</span> [...new <span class="hljs-built_in">Set</span>(intersection)]; <span class="hljs-comment">//remove duplicates</span>
     }
}
</code></pre>
<p>From the code above, we created a static <code>intersection()</code> method with an <code>arrays</code> parameter that has a spread operator. Within the function, we'll return an empty array if the method is called without any parameter. </p>
<p>Next, we use the <code>reduce()</code> method to recursively iterate over the arrays and filter the common elements at each step, until it finds the intersection of all arrays. </p>
<p>Then we return the final result as an array after removing the duplicates with a <code>Set</code> constructor function.</p>
<h3 id="heading-the-remove-function">The <code>_.remove()</code> function</h3>
<p>The <code>remove()</code> function removes some elements that satisfy a condition and returns the result. It also permanently removes those elements from the original array. </p>
<p>For example, if you have an array of numbers and you want to remove all occurrences of a certain value, you can use <code>remove()</code> to achieve this efficiently.</p>
<p>According to the <a target="_blank" href="https://lodash.com/docs/#remove">Lodash documentation</a>, here’s how it works:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> array = [<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">4</span>,<span class="hljs-number">5</span>,<span class="hljs-number">6</span>,<span class="hljs-number">7</span>];

<span class="hljs-keyword">let</span> odd = _.remove(array, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
     <span class="hljs-keyword">return</span> n%<span class="hljs-number">2</span> !== <span class="hljs-number">0</span>
});

<span class="hljs-built_in">console</span>.log(odd)
<span class="hljs-comment">//[1,2,5,7]</span>

<span class="hljs-built_in">console</span>.log(array)
<span class="hljs-comment">//returns the remaining =&gt; [2,4,6]</span>
</code></pre>
<p>Here’s how to implement it yourself:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_</span> </span>{
     <span class="hljs-comment">//... other methods</span>

     <span class="hljs-keyword">static</span> remove(array, predicate){
           <span class="hljs-keyword">let</span> truthy = array.filter(predicate);
           <span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> i <span class="hljs-keyword">of</span> truthy){
                 <span class="hljs-keyword">let</span> n = array.indexOf(i)
                 array.splice(n, <span class="hljs-number">1</span>); 
           } 
           <span class="hljs-keyword">return</span> truthy;
     }
}
</code></pre>
<p>From the code above, we created a static <code>remove()</code> method that contains two parameters: <code>array</code> and <code>predicate</code>. <code>array</code> refers to the array whose elements will be removed while <code>predicate</code> is a function that'll specify the conditions the elements must pass to be removed.</p>
<p>Next, we use the <code>array.filter()</code> method to filter out elements that pass the conditions. Then we used loops and <code>array.splice()</code> to remove the passed element from the original array. Finally, we return the truthy array.</p>
<h3 id="heading-the-union-method">The <code>_.union()</code> method</h3>
<p>The <code>union()</code> method returns an array of unique values from one or more arrays.</p>
<p>Imagine you have several arrays representing different categories of items, and you want to combine them into a single array without repeating any items. By using the <code>union()</code> method, you can easily merge these arrays, ensuring that each value appears only once in the resulting array.</p>
<p>This is valuable when dealing with data from various sources that might contain overlapping information.</p>
<p>According to <a target="_blank" href="https://lodash.com/docs/#union">Lodash</a>, here is how it works:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> basket1 = [<span class="hljs-string">'Egg'</span>, <span class="hljs-string">'Shoe'</span>, <span class="hljs-string">'Milk'</span>];
<span class="hljs-keyword">let</span> basket2 = [<span class="hljs-string">'Shoe, '</span>Milk<span class="hljs-string">', '</span>Honey<span class="hljs-string">'];

let allBasket = _.union(basket1, basket2)
console.log(allBasket)
//['</span>Egg<span class="hljs-string">', '</span>Shoe<span class="hljs-string">', '</span>Milk<span class="hljs-string">', '</span>Honey<span class="hljs-string">']</span>
</code></pre>
<p>Here’s how to implement it yourself:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_</span> </span>{
    <span class="hljs-comment">//other methods ...</span>

    <span class="hljs-keyword">static</span> union(...arrays){
          <span class="hljs-keyword">let</span> total = []
          <span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> i <span class="hljs-keyword">of</span> arrays){
                total.push(...i)
          }
          <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Set</span>(total);
    }
}
</code></pre>
<p>In the code above, we created a static <code>union()</code> method with an <code>arrays</code> parameter that has a spread operator. We created a variable storing an empty array and used the <code>for-of</code> loop to push elements in the <code>arrays</code> parameter to <code>total</code>. </p>
<p>Next, we insert the total variable into a <code>Set</code> function to remove duplicate and return it.</p>
<h2 id="heading-how-to-create-collection-methods">How to Create Collection Methods</h2>
<h3 id="heading-the-filter-method">The <code>_.filter()</code> method</h3>
<p>In Lodash, the <code>filter()</code> method returns an array of elements that satisfies a condition. Unlike <code>remove()</code>, it doesn’t modify the original array.</p>
<p>Its applications range from data filtering to creating custom subsets. </p>
<p>For example, if you have an array of objects representing users, and you want to retrieve all users who are active. By using the <code>filter()</code> method, you can efficiently create a new array containing only the active users.</p>
<p>Another example is using this method for data transformation scenarios. If you have an array of numeric values and you want to extract only the even numbers, you can easily achieve this by applying the <code>filter()</code> method with a custom filtering function.</p>
<p>According to the <a target="_blank" href="https://lodash.com/docs/#filter">Lodash documentation</a>, this is how it works:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> users = [
    {<span class="hljs-attr">name</span>: <span class="hljs-string">'Zoe'</span>, <span class="hljs-attr">age</span>: <span class="hljs-number">24</span>, <span class="hljs-attr">active</span>: <span class="hljs-literal">false</span>},
    {<span class="hljs-attr">name</span>: <span class="hljs-string">'Aisha'</span>, <span class="hljs-attr">age</span>: <span class="hljs-number">20</span>, <span class="hljs-attr">active</span>: <span class="hljs-literal">true</span>},
    {<span class="hljs-attr">name</span>: <span class="hljs-string">'Alex'</span>, <span class="hljs-attr">age</span>: <span class="hljs-number">19</span>, <span class="hljs-attr">active</span>: <span class="hljs-literal">true</span>}
];
filter(users, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">element</span>)</span>{ <span class="hljs-keyword">return</span> element.active &gt; <span class="hljs-literal">true</span>})

<span class="hljs-comment">//returns =&gt; [</span>
    {<span class="hljs-attr">name</span>: <span class="hljs-string">'Aisha'</span>, <span class="hljs-attr">age</span>: <span class="hljs-number">20</span>, <span class="hljs-attr">active</span>: <span class="hljs-literal">true</span>},
    {<span class="hljs-attr">name</span>: <span class="hljs-string">'Alex'</span>, <span class="hljs-attr">age</span>: <span class="hljs-number">19</span>, <span class="hljs-attr">active</span>: <span class="hljs-literal">true</span>}
]
</code></pre>
<p>Here’s how to implement it on your own:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_</span> </span>{
     <span class="hljs-comment">//... other methods</span>

     <span class="hljs-keyword">static</span> filter(collection, predicate){
           <span class="hljs-keyword">return</span> collection.filter(predicate);
    }
}
</code></pre>
<p>From the code above, we created a static <code>filter()</code> method with an <code>array</code> and <code>predicate</code> parameter. <code>array</code> is the array whose elements will be filtered and <code>predicat</code>e is the function that contains the conditions each element must pass to be filtered out. </p>
<p>Then we use the <code>array.filter()</code> method to filter the array and return the result.</p>
<h3 id="heading-the-find-method">The <code>_.find()</code> method</h3>
<p>The <code>find()</code> method returns the first element in an array that satisfies a particular condition.</p>
<p>Its applications range from targeted searches to efficient data retrieval.</p>
<p>For example, say you have an array of objects representing products in an online store, and you want to find the first product that is currently on sale. By using the <code>find(</code>) method, you can quickly locate the desired product object that matches the sale condition.</p>
<p>The <code>find()</code> method also plays a crucial role in scenarios where you need to search for a specific item within a collection, such as finding a particular user by their username or locating a book by its title. </p>
<p>According to the <a target="_blank" href="https://lodash.com/docs/#find">Lodash documentation</a>, this is how it works:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> products = [
     {<span class="hljs-attr">name</span>: <span class="hljs-string">'Rice'</span>, <span class="hljs-attr">qty</span>: <span class="hljs-number">20</span>},
     {<span class="hljs-attr">name</span>: <span class="hljs-string">'Egg'</span>, <span class="hljs-attr">qty</span>: <span class="hljs-number">24</span>},
     {<span class="hljs-attr">name</span>: <span class="hljs-string">'Milk'</span>, <span class="hljs-attr">qty</span>: <span class="hljs-number">19</span>},
     {<span class="hljs-attr">name</span>: <span class="hljs-string">'Wheat'</span>, <span class="hljs-attr">qty</span>: <span class="hljs-number">20</span>}
]

<span class="hljs-comment">//finds the first product whose quantity is greater than 20</span>
_.find(products, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">product</span>)</span>{ <span class="hljs-keyword">return</span> product.qty &gt; <span class="hljs-number">20</span>})

<span class="hljs-comment">//returns {name: 'Egg', qty: 24}</span>
</code></pre>
<p>Here's how to implement it yourself:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_</span> </span>{
    <span class="hljs-comment">//... other methods</span>

     <span class="hljs-keyword">static</span> find(collection, predicate, fromIndex=<span class="hljs-number">0</span>){
           <span class="hljs-keyword">let</span> ans = collection.slice(fromIndex, collection.length)
           <span class="hljs-keyword">return</span> ans.find(predicate);
     }
}
</code></pre>
<p>In the code above, we created a static <code>find()</code> method with 3 parameters: <code>collection</code>, <code>predicate</code>, <code>fromIndex</code>. </p>
<p><code>collection</code> is the array we’ll be searching from. <code>predicate</code> is a function containing the condition that the element must pass to be returned. <code>fromIndex</code> specifies the index to begin the search from. </p>
<p>First, we used the <code>array.slice()</code> method to slice the array from a start position specified by <code>fromIndex</code> parameter to the end of the collection. Next, we use the <code>array.find()</code> method on the sliced array and return the result.</p>
<h3 id="heading-the-partition-method">The <code>_.partition()</code> method</h3>
<p>The <code>partition()</code> method creates an array comprised of two elements. The first is an array of elements that satisfies a certain condition while the second is an array of elements that doesn’t satisfy the condition.</p>
<p>Its applications range from data segregation to creating distinct subsets.</p>
<p>Imagine you have an array of numbers, and you want to separate the even numbers from the odd numbers. By using the <code>partition()</code> method, you can easily create two arrays: one containing all the even numbers and another containing all the odd numbers. This makes it convenient to perform separate operations or analyses on each subset.</p>
<p>Also, the <code>partition()</code> method is beneficial when dealing with filtering and categorization scenarios. If you have a collection of objects representing products and you want to categorize them into two groups – those that are on sale and those that are not – the <code>partition()</code> method provides an elegant solution.</p>
<p>According to the <a target="_blank" href="https://lodash.com/docs/#partition">Lodash documentation</a>, this is how it works:</p>
<pre><code class="lang-javascript"> <span class="hljs-keyword">let</span> products = [
     {<span class="hljs-attr">name</span>: <span class="hljs-string">'Milk'</span>, <span class="hljs-attr">sold</span>: <span class="hljs-literal">true</span>},
     {<span class="hljs-attr">name</span>: <span class="hljs-string">'Cream'</span>, <span class="hljs-attr">sold</span>: <span class="hljs-literal">false</span>},
     {<span class="hljs-attr">name</span>: <span class="hljs-string">'Bicycle'</span>, <span class="hljs-attr">sold</span>: <span class="hljs-literal">true</span>},
     {<span class="hljs-attr">name</span>: <span class="hljs-string">'Socks'</span>, <span class="hljs-attr">sold</span>: <span class="hljs-literal">false</span>}
 ];

_.partition(products, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>)</span>{ <span class="hljs-keyword">return</span> e.sold === <span class="hljs-literal">true</span>})
returns [
     [
    {<span class="hljs-attr">name</span>: <span class="hljs-string">'Milk'</span>, <span class="hljs-attr">sold</span>: <span class="hljs-literal">true</span>},
    {<span class="hljs-attr">name</span>: <span class="hljs-string">'Bicycle'</span>, <span class="hljs-attr">sold</span>: <span class="hljs-literal">true</span>}
],
     [
     {<span class="hljs-attr">name</span>: <span class="hljs-string">'Cream'</span>, <span class="hljs-attr">sold</span>: <span class="hljs-literal">false</span>},
     {<span class="hljs-attr">name</span>: <span class="hljs-string">'Socks'</span>, <span class="hljs-attr">sold</span>: <span class="hljs-literal">false</span>}
]
]
</code></pre>
<p>Here’s how to implement it yourself:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_</span> </span>{
    <span class="hljs-comment">//... other methods</span>

    <span class="hljs-keyword">static</span> partition(collection, predicate){
        <span class="hljs-keyword">let</span> truthy = array.filter(predicate);
        <span class="hljs-keyword">let</span> falsy = collection;

        <span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> i <span class="hljs-keyword">of</span> truthy){
            <span class="hljs-keyword">let</span> n = falsy.indexOf(i)
            falsy.splice(n, <span class="hljs-number">1</span>); 
        } 

        <span class="hljs-keyword">return</span> [truthy, falsy];
    }
}
</code></pre>
<p>In the code above, we created a static <code>partition()</code> method with a <code>collection</code> and <code>predicate</code> parameter. <code>collection</code> is the array that’ll be used while <code>predicate</code> is the function that contains the conditions the elements must pass to be partitioned to the first element. </p>
<p>First, we created two variables: <code>truthy</code> and <code>falsy</code>. <code>truthy</code> is an array containing the elements that passed the condition while <code>falsy</code> stores the collection. </p>
<p>Next, we used a <code>for-of</code> loop and the <code>splice()</code> method to remove the elements in the <code>falsy</code> array from <code>truthy</code>. And finally, we returned an array containing both <code>truthy</code> and <code>falsy</code> arrays.</p>
<h3 id="heading-the-shuffle-method">The <code>_.shuffle()</code> method</h3>
<p>The <code>shuffle()</code> method returns an a shuffled array using the Fisher-Yates shuffle.</p>
<p>Its applications range from enhancing user engagement to introducing randomness into various scenarios</p>
<p>Imagine you have an array of questions for a quiz app, and you want to present the questions in a different order each time a user takes the quiz. By using the <code>shuffle()</code> method, you can easily create a new array with the questions in a randomized order, providing a fresh experience for users in each quiz session.</p>
<p>According to <a target="_blank" href="https://lodash.com/docs/#shuffle">Lodash</a>, this is how it works:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> quizQuestions = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>];

_.shuffle(quizQuestions)
<span class="hljs-comment">//shuffled version eg [2,3,8,5,1,7,4,6]</span>
</code></pre>
<p>Here's how to implement it yourself:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_</span> </span>{
    <span class="hljs-comment">//... other methods</span>

     <span class="hljs-keyword">static</span> shuffle(collection){
           <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sh</span>(<span class="hljs-params">array=collection, shuffled=[], length=collection.length</span>)</span>{
                 <span class="hljs-keyword">if</span>(length === <span class="hljs-number">0</span>){
                      <span class="hljs-keyword">return</span> shuffled;
                 }

                 <span class="hljs-keyword">let</span> rand = <span class="hljs-built_in">Math</span>.floor( <span class="hljs-built_in">Math</span>.random() * ( length - <span class="hljs-number">1</span>) );

                 shuffled.push( array[rand] )

                length -= <span class="hljs-number">1</span>;

                       array.splice(rand, <span class="hljs-number">1</span>);

                 <span class="hljs-keyword">return</span> sh(array, shuffled, length);

           }
           <span class="hljs-keyword">return</span> sh() 

     }
}
</code></pre>
<p>In the code above, we created a static <code>shuffle()</code> method with a <code>collection</code> parameter that represents the array to shuffle. Within it is another function <code>sh()</code> that uses recursion to shuffle the array.</p>
<h2 id="heading-how-to-create-math-methods">How to Create Math Methods</h2>
<h3 id="heading-the-mean-method">The <code>_.mean()</code> method</h3>
<p>In Lodash, the <code>mean()</code> method calculates the average value of the elements in an array.</p>
<p>This method can be used for performing data analysis.</p>
<p>Imagine you have an array of test scores, and you want to find out the average score of the students. By using the <code>mean()</code> method, you can effortlessly calculate the average test score, which gives you a sense of the overall performance of the group.</p>
<p>Furthermore, the <code>mean()</code> method plays a crucial role in data analysis and statistics.</p>
<p>Whether you're working with financial data, scientific measurements, or any other numerical data, calculating the mean helps you understand the typical value and make informed decisions based on the data's central tendency.</p>
<p>According to the <a target="_blank" href="https://lodash.com/docs/#mean">Lodash documentation</a>, this is how it works:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> mathScore = [<span class="hljs-number">60</span>, <span class="hljs-number">70</span>, <span class="hljs-number">50</span>, <span class="hljs-number">80</span>];

_.mean(mathScore)
returns <span class="hljs-number">65</span>
</code></pre>
<p>Here's how to implement it yourself:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_</span> </span>{
    <span class="hljs-comment">//... other methods</span>

     <span class="hljs-keyword">static</span> mean(array){
           <span class="hljs-keyword">let</span> sum = array.reduce( <span class="hljs-function">(<span class="hljs-params">accumulator, current</span>) =&gt;</span> {
                 <span class="hljs-keyword">return</span> accumulator + current
           }, <span class="hljs-number">0</span>);
           <span class="hljs-keyword">return</span> sum/array.length;
    }
}
</code></pre>
<p>In the code above, we created a static <code>mean()</code> method with an <code>array</code> parameter that represents the array whose elements we'll use to calculate the mean. </p>
<p>Next, we use the <code>array.reduce()</code> method to find the sum of all the values. </p>
<p>Finally, we divide the result by the length of the array and return it.</p>
<h3 id="heading-the-max-method">The <code>_.max()</code> method</h3>
<p>The <code>max()</code> method returns the maximum value in an array.</p>
<p>Its applications range from identifying the highest values to detecting outliers.</p>
<p>The <code>max()</code> method is crucial in scenarios where you need to identify extreme values in a dataset.</p>
<p>Whether you're analyzing temperature readings, stock prices, or any other numeric data, finding the maximum value can help you understand the range and potential outliers within the data.</p>
<p>According to the <a target="_blank" href="https://lodash.com/docs/#max">Lodash docs</a>, here's how it works:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> topStockPrices = [<span class="hljs-number">545</span>, <span class="hljs-number">230</span>, <span class="hljs-number">123</span>, <span class="hljs-number">1004</span>, <span class="hljs-number">890</span>,<span class="hljs-number">890</span>];

_.max(topStockPrices)
<span class="hljs-comment">//1004</span>
</code></pre>
<p>Here's how to implement it:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_</span> </span>{
    <span class="hljs-comment">//... other methods</span>

     <span class="hljs-keyword">static</span> max(array){
           <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.max(...array)
     }
}
</code></pre>
<p>In the code above, we created a static <code>mean()</code> method with an array <code>parameter</code> that represents the array whose elements will be used to calculate the max value. </p>
<p>Next, we use the spread operator and <code>Math.max()</code> function to calculate the maximum value.</p>
<h3 id="heading-the-min-method">The <code>_.min()</code> method</h3>
<p>The <code>_.min()</code> method returns the minimum value in an array.</p>
<p>The <code>min()</code> method is crucial in scenarios where you need to identify the smallest value in a dataset.</p>
<p>Whether you're analyzing prices, durations, or any other numeric data, finding the minimum value can help you understand the range and potential outliers within the data.</p>
<p>According to the <a target="_blank" href="https://lodash.com/docs/#min">Lodash docs</a>, here's how it works:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> productPrice = [<span class="hljs-number">200</span>, <span class="hljs-number">150</span>, <span class="hljs-number">500</span>, <span class="hljs-number">230</span>, <span class="hljs-number">99</span>];

_.min(productPrice)
<span class="hljs-comment">//returns 99</span>
</code></pre>
<p>Here's how to implement it:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_</span> </span>{
    <span class="hljs-comment">//...other methods</span>

     <span class="hljs-keyword">static</span> max(array){
           <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.min(...array)
     }
}
</code></pre>
<p>In the code above, we created a static <code>min()</code> method with an <code>array</code> parameter that represents the array whose elements will be used to calculate the min. </p>
<p>Next, we use the spread operator and <code>Math.min()</code> function to calculate the minimum value.</p>
<h3 id="heading-the-sum-method">The <code>_.sum()</code> method</h3>
<p>The <code>sum()</code> method calculates the sum of all the elements in an array.</p>
<p>This method can be used for calculating aggregate values.</p>
<p>For example, the <code>sum()</code> method is crucial in scenarios where you need to determine the aggregate value of a dataset.</p>
<p>Whether you're working with financial transactions, quantities, or any other numeric data, calculating the sum helps you understand the overall magnitude of the data.</p>
<p>According to the <a target="_blank" href="https://lodash.com/docs/#sum">Lodash docs</a>, here’s how it works:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> sales = [<span class="hljs-number">20000</span>, <span class="hljs-number">34000</span>, <span class="hljs-number">21000</span>, <span class="hljs-number">15000</span>];

_.sum(sales)
<span class="hljs-comment">//returns -&gt; 90000</span>
</code></pre>
<p>Here’s how to implement it:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_</span> </span>{
    <span class="hljs-comment">//... other methods</span>

     <span class="hljs-keyword">static</span> sum(array){
           <span class="hljs-keyword">let</span> total = array.reduce( <span class="hljs-function">(<span class="hljs-params">accumulator, current</span>) =&gt;</span> {
                 <span class="hljs-keyword">return</span> accumulator + current
           }, <span class="hljs-number">0</span>);
           <span class="hljs-keyword">return</span> total;
     }
}
</code></pre>
<p>In the code above, we created a static <code>sum()</code> method with an <code>array</code> parameter. Next, we used the <code>array.reduce()</code> method to add up all the array element's values.</p>
<h2 id="heading-how-to-create-object-methods">How to Create Object Methods</h2>
<h3 id="heading-the-keys-method">The <code>_.keys()</code> method</h3>
<p>In Lodash, the <code>keys()</code> method returns an array containing the properties of an object.</p>
<p>Imagine you have an object representing a user profile with properties like name, email, and age. By using the <code>keys()</code> method, you can easily extract the property names and work with them.</p>
<p>According to the <a target="_blank" href="https://lodash.com/docs/#keys">Lodash docs</a>, here’s how it works:</p>
<pre><code class="lang-javascript">returns all the properties <span class="hljs-keyword">of</span> th object
_.keys({<span class="hljs-attr">name</span>: <span class="hljs-string">'john'</span>, <span class="hljs-attr">age</span>: <span class="hljs-number">7</span>})
<span class="hljs-comment">//['name', 'age']</span>
</code></pre>
<p>Here's how to implement it:</p>
<pre><code class="lang-javascript">Class _{
     <span class="hljs-keyword">static</span> keys(object){
           <span class="hljs-keyword">return</span> <span class="hljs-built_in">Object</span>.keys(object)
    }
}
</code></pre>
<p>In the code above, we created a static <code>keys()</code> method with an <code>object</code> parameter that represents the object whose properties would be extracted. </p>
<p>Then we use the <code>Object.keys()</code> method to extract the properties and return the result.</p>
<h3 id="heading-the-values-method">The <code>_.values()</code> method</h3>
<p>The <code>values()</code> method returns an array containing the values of an object's properties.</p>
<p>Imagine you have an object representing a user profile with properties like name, email, and age. By using the <code>values()</code> method, you can easily extract the property values and work with them.</p>
<p>According to the <a target="_blank" href="https://lodash.com/docs/#values">Lodash docs</a>, here's how it works:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//returns the object values</span>
_.values({<span class="hljs-attr">name</span>: <span class="hljs-string">'john'</span>, <span class="hljs-attr">age</span>: <span class="hljs-number">7</span>})
<span class="hljs-comment">//['john', 7]</span>
</code></pre>
<p>Here's how to implement it:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_</span></span>{
     <span class="hljs-keyword">static</span> values(){
           <span class="hljs-keyword">return</span> <span class="hljs-built_in">Object</span>.values(object)
    }
}
</code></pre>
<p>In the code above, we created a <code>values()</code> method with an <code>object</code> parameter that represents the object whose property values would be extracted. Then we use the <code>Object.values()</code> method to extract the values and returned the result.</p>
<h2 id="heading-how-to-create-string-methods">How to Create String Methods</h2>
<h3 id="heading-the-repeat-method">The <code>_.repeat()</code> method</h3>
<p>In Lodash, the <code>repeat()</code> method returns a string that has been duplicated a specific number of times.</p>
<p>Its applications range from creating patterns to formatting output, making it a handy tool for various string manipulation tasks.</p>
<p>For instance, if you want to create a separator line of hyphens in a console output, you can use the <code>repeat()</code> method to generate the necessary number of hyphens.</p>
<p>According to <a target="_blank" href="https://lodash.com/docs/#repeat">Lodash</a>, here is how it works:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//repeats the string '-' 10 times</span>

_.repeat(<span class="hljs-string">'-'</span>, <span class="hljs-number">10</span>)
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'hello world'</span>)
_.repeat(<span class="hljs-string">'-'</span>, <span class="hljs-number">10</span>)

<span class="hljs-comment">//returns:</span>
<span class="hljs-comment">//'----------'</span>
<span class="hljs-comment">//'hello world'</span>
<span class="hljs-comment">//'----------'</span>
</code></pre>
<p>Here's how to implement it:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_</span> </span>{ 
    <span class="hljs-comment">//... other methods</span>

     <span class="hljs-keyword">static</span> repeat(string=<span class="hljs-string">''</span>, n=<span class="hljs-number">1</span>){
           <span class="hljs-keyword">let</span> repeated =<span class="hljs-string">''</span>;

           <span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; n; i++){
                 repeated += string;
           }
           <span class="hljs-keyword">return</span> repeated;
     }
}
</code></pre>
<p>In the code above, we created a static <code>repeat()</code> method with a string and n parameter. <code>string</code> refers to the string that’ll be duplicated while <code>n</code> specifies the number of times it’ll be repeated.</p>
<p>Next, we created a <code>repeated</code> variable that stores an empty string. ANd finally, we used the <code>for</code> loop to concatenate the string <code>n</code> times.</p>
<h3 id="heading-the-split-method">The <code>_.split()</code> method</h3>
<p>The <code>split()</code> method splits a string by a separator and stores the parts in an array.</p>
<p>Its applications range from text processing to data extraction.</p>
<p>The <code>split()</code> method can be beneficial in scenarios where you're dealing with data in a specific format.</p>
<p>For example, if you have a list of values in a string separated by plus sign(+), you can use the <code>split()</code> method to extract each value and store them in an array.</p>
<p>According to the <a target="_blank" href="https://lodash.com/docs/#split">Lodash docs</a>, here’s how it works:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//Splits all the characters</span>
_.split(<span class="hljs-string">'hello'</span>, <span class="hljs-string">''</span>)
[<span class="hljs-string">'h'</span>, <span class="hljs-string">'e'</span>, <span class="hljs-string">'l'</span>, <span class="hljs-string">'l'</span>, <span class="hljs-string">'o'</span>]

<span class="hljs-comment">//Split using _ as separator </span>
_.split(<span class="hljs-string">'h_e_l_l_o'</span>, <span class="hljs-string">'_'</span>)
[<span class="hljs-string">'h'</span>, <span class="hljs-string">'e'</span>, <span class="hljs-string">'l'</span>, <span class="hljs-string">'l'</span>, <span class="hljs-string">'o'</span>]

Splits the string using + <span class="hljs-keyword">as</span> a separator and limits the elements to <span class="hljs-number">2</span>
_.split<span class="hljs-string">'`how+to+cook+rice'</span>, <span class="hljs-string">'+'</span>, <span class="hljs-number">2</span>)
[<span class="hljs-string">'how'</span>, <span class="hljs-string">'to'</span>]
</code></pre>
<p>Here's how to implement it:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_</span> </span>{
    <span class="hljs-comment">//... other methods</span>

     <span class="hljs-keyword">static</span> split(string=<span class="hljs-string">''</span>, separator, limit){
           <span class="hljs-keyword">let</span> spl = string.split(separator);
           <span class="hljs-keyword">let</span> limited = [];

           <span class="hljs-keyword">if</span> (limit === <span class="hljs-literal">undefined</span>){
                 <span class="hljs-keyword">return</span> spl;
           } <span class="hljs-keyword">else</span> {
                 <span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> i=<span class="hljs-number">0</span>; i&lt;limit; i++){
                       limited.push(spl[i])
                 }
                 <span class="hljs-keyword">return</span> limited

            }
     }
}
</code></pre>
<p>In the code above, we created a <code>split()</code> method with a <code>string</code>, <code>separator</code>, and <code>limit</code> parameter. </p>
<p><code>string</code> is the string whose characters will be split. <code>separator</code> will be used as the string separator and <code>limit</code> sets a limit for the split characters to be returned. </p>
<p>Next, we used the <code>string.split()</code> method to split string with separator. Then we returned the split string if no limit was specified. If the limit was specified, we used the <code>for</code> loop to push the selected elements to a <code>limited</code> variable and returned its result.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Phew – that was a lot. If you made it this far, let me congratulate you on time well spent. </p>
<p>Throughout the article, you learned how to implement some functionalities of the Lodash library. </p>
<p>Now you can challenge yourself to complete the library by implementing the remaining methods and functions, or add more functionalities to it. </p>
<p>I hope this helps. Unrelated, but if you need a skilled technical writer don’t forget to contact me through <a target="_blank" href="https://twitter.com/gidthecoder">Twitter(@GidtheCoder)</a> or <a target="_blank" href="mailto:akinsanmi20700@gmail.com">email(akinsanmi20700@gmail.com)</a>. Bye for now.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use the JavaScript Fullscreen API ]]>
                </title>
                <description>
                    <![CDATA[ The Fullscreen API is a browser web API that allows you to enable fullscreen mode for HTML elements. It saves you the stress of using CSS and JavaScript to implement fullscreen functionality. The use cases of fullscreen API are numerous because of th... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-the-javascript-fullscreen-api/</link>
                <guid isPermaLink="false">66bb54d20da5b03e481107b6</guid>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gideon Akinsanmi ]]>
                </dc:creator>
                <pubDate>Mon, 10 Jul 2023 21:02:06 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/07/js_fullscreen_png.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>The Fullscreen API is a browser web API that allows you to enable fullscreen mode for HTML elements. It saves you the stress of using CSS and JavaScript to implement fullscreen functionality.</p>
<p>The use cases of fullscreen API are numerous because of the increased complexity of today's websites. In this article I'll be giving you a comprehensive guide to using the Fullscreen API.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>You should have basic knowledge of how to use HTML, DOM and JavaScript to create webpages. Specifically, you should know how to use HTML elements, DOM selector methods, event listeners, and JavaScript objects.</p>
<h2 id="heading-fullscreen-api-basics">Fullscreen API Basics</h2>
<p>The Fullscreen API has functionalities that let you display your elements (mostly images, media, and graphic elements) in fullscreen mode. Without it, you'd need to get your hands dirty with some long lines of CSS and JavaScript code.</p>
<p>Have you ever visited a website where the images and videos go fullscreen when you interact with them? What about online games?</p>
<p>Let me explain some 3 real-world projects that use fullscreen mode. Although they might not be using the Fullscreen API under the hood, I'm trying to give you a practical view of the use cases of this functionality and how using the Fullscreen API can simplify the process. They include: the Twitter web app, an online gaming website, and YouTube video embeds.</p>
<p>An example of a project that uses fullscreen mode is the <a target="_blank" href="https://twitter.com">Twitter web app</a>. Whenever you log in to your Twitter account and click on any image that interests you, it goes fullscreen. This functionality could have been implemented with hundreds of lines of code. But with fFullscreen API, you wouldn't need that much code.‌</p>
<p>The same goes for online gaming websites. Whenever you visit a gaming website like <a target="_blank" href="https://crazygames.com">crazygames.com</a> and click on any game you feel like playing, the canvas element that stores the game source code goes fullscreen.‌</p>
<p>‌Another example is the YouTube video embeds. If you are trying to embed a YouTube video on your website by using the iframe element with the <code>allow='fullscreen'</code>  attribute present, it allows your YouTube video go fullscreen when a user clicks on the fullscreen icon. Check out my <a target="_blank" href="https://codepen.io/gid-droid/pen/JjerqPg">codepen link</a> and click on the fullscreen icon to observe the behavior.</p>
<p>Natively, the video element is the only HTML element that has capabilities by default. Whenever you create a video element with a controls attribute, it automatically gets a fullscreen control icon that allows you to toggle your video between fullscreen and normal mode. You can try it yourself.</p>
<p>Although the fullscreen API can be used on all major desktop and mobile browsers, sometimes it’s best practice to use multiple prefixed versions of some properties (for document.fullscreenElement there's <code>mozFullScreenElement</code> for Mozilla Firefox, or <code>webkitIsFullScreen</code>). If you have any doubts, you can check the prefix table on <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API/Guide#prefixing">MDN</a>.</p>
<h2 id="heading-fullscreen-api-properties-and-methods">Fullscreen API Properties and Methods</h2>
<p>To successfully use the Fullscreen API, ‌ need to be familiar with its properties and methods. They include:</p>
<ul>
<li><code>**element.requestFullscreen()**</code>: This method tells the web browser to set a specified element in fullscreen mode.</li>
<li><code>**document.exitFullscreen()**</code>: This method tells the web browser to exit an element from fullscreen mode and return to normal mode.</li>
<li><strong><code>document.fullscreenElement</code></strong>: This property returns the element that is in fullscreen or checks if fullscreen mode is active. A value of null means no element is in fullscreen mode.</li>
<li><code>**document.fullscreenEnabled**</code>: This property checks whether the fullscreen mode is supported. It returns <code>true</code> if fullscreen mode is supported and <code>false</code> if it isn’t.</li>
<li><code>**fullscreenchange**</code> event: This event is used for detecting changes in the fullscreen mode. It can be used to create some functionality when fullscreen mode is activated and exited.</li>
</ul>
<h2 id="heading-how-to-request-fullscreen-mode">How to Request Fullscreen Mode</h2>
<p>The first step in implementing this functionality is to request fullscreen mode. You can do this with the <code>requestFullscreen()</code> method.</p>
<p>Here is the code to request fullscreen mode:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> image = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'image.img'</span>); 

image.requestFullscreen();
</code></pre>
<p>‌The above code will return an error because fullscreen can only be activated through user interactions such as clicks, double clicks, and so on.</p>
<p>In the code below, we’ll use a click event to activate the fullscreen mode.</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> image = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'img.image'</span>);

image.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>)</span>{
      image.requestFullscreen();
})
</code></pre>
<p>‌In the code above, I used a <code>querySelector()</code> method to select an img element. Then I attached a click event listener to make the image go fullscreen when clicked.</p>
<h2 id="heading-how-to-exit-fullscreen-mode">How to Exit Fullscreen Mode</h2>
<p>After the fullscreen mode has been activated, you'll need an <code>exitFullscreen()</code> method to exit the fullscreen mode.</p>
<p>Here’s the code for exiting fullscreen mode:</p>
<p><code>document.exitFullscreen();</code></p>
<p>In a real-world project, you'll need a user to interact with the web page in order to exit fullscreen. You'll need an event listener that listens to user interactions like clicks, double clicks, keydown, and so on.</p>
<p>Here’s how you use an event listener with the <code>exitFullscreen()</code> function:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> image = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'img.image'</span>); 

image.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>)</span>{ 
     image.requestFullscreen(); 
 }) 

 image.addEventListener(<span class="hljs-string">'dblclick'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>)</span>{ 
      <span class="hljs-built_in">document</span>.exitFullscreen(); 
})
</code></pre>
<p>From the code above, when a user clicks on the image, the fullscreen mode gets activated. When you double-click on the image, it gets deactivated.</p>
<h2 id="heading-how-to-check-fullscreen-state">How to Check Fullscreen State</h2>
<p>To check if an element is in fullscreen mode or not, we’ll use the <code>fullscreenElement</code> property.</p>
<p>A value of <code>null</code> means it's not in fullscreen mode.</p>
<p>From the example above, you can modify the code to activate and exit fullscreen mode based on the value of the <code>fullscreenElement</code> property.</p>
<p>Here’s how to use this property:</p>
<pre><code class="lang-js">image.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>)</span>{ 

   <span class="hljs-keyword">if</span>(<span class="hljs-built_in">document</span>.fullscreenElement){ 
      <span class="hljs-built_in">document</span>.exitFullscreen() 
   } <span class="hljs-keyword">else</span> { 
     image.requestFullscreen();
   } 

})
</code></pre>
<p>From the above code, you’ve instructed the browser to toggle between full-screen and normal mode when the image is clicked.</p>
<h2 id="heading-event-handling-and-fullscreen-state">Event handling and Fullscreen State</h2>
<p>Sometimes you might want to make some changes (such as alerting the user) when fullscreen mode is activated or exited.</p>
<p>Instead of using the document.fullscreenElement and multiple event handlers to check if an element is in fullscreen, you can use the ‘fullscreenchange’ event .</p>
<p>Here’s the code:</p>
<pre><code class="lang-js"><span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">'fullscreenchange'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>)</span>{ 

   <span class="hljs-keyword">if</span>(<span class="hljs-built_in">document</span>.fullscreenElement){ 
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'fullscreen mode activated'</span>);
   } <span class="hljs-keyword">else</span> { 
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'fullscreen mode deactivated'</span>); 
   } 

})
</code></pre>
<p>From the code above, anytime fullscreen mode is activated or exited from any element, a console.log() message is displayed in the developers tool.</p>
<h2 id="heading-how-to-apply-fullscreen-to-various-element-types">How to Apply Fullscreen to Various Element Types</h2>
<p>Although fullscreen can be applied to all elements, it’s best used on media and interactive elements such as img, video, iframe, and canvas.</p>
<p>Although the use cases are numerous, I'll be explaining a few.</p>
<p>For your canvas element, if you are building an online game, it can be used to make your games fullscreen. Also, if you are building a dashboard with a canvas-based charting library, it can be used to make individual charts in your dashboard go fullscreen when the user clicks on them.</p>
<p>For your video element, if you are building a custom video player for your project, you can use it to implement fullscreen functionality.</p>
<p>For your image element, if you're building a web app (such as a social media platform) that supports image and video upload, you can use this API to implement fullscreen mode on the elements.</p>
<p>For iframe, embed and object element, if you are building an image-to-pdf converter website, you can use this API to make your converted PDF go fullscreen for reviewal by your users.</p>
<h2 id="heading-fullscreen-api-best-practices-and-tips">Fullscreen API Best Practices and Tips</h2>
<p>Here are the best practices and tips for fullscreen API:</p>
<ol>
<li>Provide user-friendly controls for entry and exit: if you are implementing fullscreen on images, you should activate it when the user clicks on the image and exit when the user double tab or presses the backspace button. Similar user-friendly methods can be used on video and canvas elements. Also, make sure you notify the user when they enter and exit fullscreen.</li>
<li>Ensure fallback options for non-supported browsers: If a user is trying to activate fullscreen from an unsupported browser, you should alert them of this incompatibility.</li>
<li>If you are using fullscreen on iframes that links to external sources, don’t forget to set the <code>allow = 'fullscreen'</code> attribute.</li>
</ol>
<pre><code>&lt;button&gt;fullscreen&lt;/button&gt;
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">iframe</span> <span class="hljs-attr">allow</span>=<span class="hljs-string">'fullscreen'</span> <span class="hljs-attr">src</span>=<span class="hljs-string">'https://external.com/content'</span> <span class="hljs-attr">width</span>=<span class="hljs-string">'500'</span> <span class="hljs-attr">height</span>=<span class="hljs-string">'300'</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">iframe</span>&gt;</span></span> 

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript"> 
    <span class="hljs-keyword">let</span> button = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'button'</span>); 
    <span class="hljs-keyword">let</span> iframe = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'iframe'</span>); 

    button.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>)</span>{  
       iframe.requestFullscreen();
    }) 
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span>
</code></pre><ol start="4">
<li>Mouse events (like mouseover) and alert() function might behave unexpectedly, so you shouldn't use them within fullscreen API.</li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, you’ve learned what fullscreen API is, how to implement it, and the best practices to follow.</p>
<p> I hope that you're now able to utilize the fullscreen API capabilities in your web projects</p>
<p>Feel free to check the MDN website for further learning. You can also follow me on Twitter (@GidtheCoder) to connect with me. Cheers.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
