<?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[ Technical writing  - 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[ Technical writing  - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 05 May 2026 16:59:17 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/technical-writing-1/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Use Documentation as a Marketing Tool ]]>
                </title>
                <description>
                    <![CDATA[ I was recently moved to the marketing team at my company, and that shift has made me reflect more deeply on the role documentation plays in our go-to-market strategy. Before this move, we've had sever ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-documentation-as-a-marketing-tool/</link>
                <guid isPermaLink="false">69989fd0a20b74e093ce4fcb</guid>
                
                    <category>
                        <![CDATA[ documentation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Technical writing  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ marketing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Onyeanuna Prince ]]>
                </dc:creator>
                <pubDate>Fri, 20 Feb 2026 17:54:24 +0000</pubDate>
                <media:content url="https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/5e1e335a7a1d3fcc59028c64/2af093b1-f1aa-42cc-b33a-b842911affcd.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>I was recently moved to the marketing team at my company, and that shift has made me reflect more deeply on the role documentation plays in our go-to-market strategy.</p>
<p>Before this move, we've had several product releases, and one requirement stayed constant across all of them: the documentation had to be ready at the time of release. This wasn’t because the product was unintuitive or unusable without docs. It was because the documentation was an integral part of the product experience. A release simply wasn’t considered complete without it.</p>
<p>At a glance, this pattern can be dismissed as operational hygiene. After all, good teams ship docs alongside code. But when you look closer, it points to something more fundamental about how users actually experience products today.</p>
<p>For many products, especially developer-facing ones, documentation is often the first real interaction users have with the product. Before they sign up, before they deploy anything, and sometimes even before they talk to sales, they read the docs. This is where they assess whether the product does what it claims, whether it fits their use case, and whether it is worth investing time and effort into.</p>
<p>In practice, documentation often carries more weight than landing pages or launch blogs. Marketing content may introduce the promise of the product, but documentation is where that promise is tested. It turns positioning into reality by showing how things actually work, what trade-offs exist, and how quickly a user can succeed.</p>
<p>Seen through this lens, documentation is not just a supporting artifact but a marketing tool in its own right. It communicates product value, builds trust at the moment of highest intent, and plays a direct role in adoption.</p>
<p>In this article, I want to unpack why documentation functions this way, and why teams should start treating it as a first-class part of their go-to-market strategy rather than an afterthought.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a href="#heading-the-role-of-documentation-in-a-users-journey">The Role of Documentation in a User's Journey</a></p>
</li>
<li><p><a href="#heading-how-to-build-trust-at-the-moment-of-highest-intent">How to Build Trust at the Moment of Highest Intent</a></p>
</li>
<li><p><a href="#heading-so-what-actually-builds-trust-in-documentation">So, What Actually Builds Trust in Documentation?</a></p>
</li>
<li><p><a href="#heading-documentation-and-marketing-a-partnership">Documentation and Marketing: A Partnership</a></p>
</li>
<li><p><a href="#heading-practical-implications-treating-docs-as-marketing">Practical Implications: Treating Docs as Marketing</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-the-role-of-documentation-in-a-users-journey"><strong>The Role of Documentation in a User's Journey</strong></h2>
<p>The customer journey does not begin when someone clicks “Buy now” or "Start free trial."</p>
<p>Long before that moment, users are already forming opinions about whether a product is credible, usable, and worth their time. In traditional marketing models, this early phase is described as awareness and evaluation. But for technical products, that evaluation phase looks very different from what classic marketing funnels suggest.</p>
<p>Instead of relying primarily on demos, sales conversations, or brand messaging, technical buyers tend to self-educate. Research from <a href="https://www.gartner.com/en/newsroom/press-releases/2025-06-25-gartner-sales-survey-finds-61-percent-of-b2b-buyers-prefer-a-rep-free-buying-experience">Gartner shows that modern B2B buyers</a> spend a majority of their decision-making process researching independently before ever speaking to sales, and this behavior is even more pronounced among technical audiences who prefer primary sources over promotional material.</p>
<p>For developer tools, APIs, and infrastructure products, documentation becomes one of those primary sources. Users move from high-level marketing pages into documentation to answer concrete questions such as how the product works, what it integrates with, what assumptions it makes, and what it will realistically take to adopt. This transition from marketing to documentation is a natural progression in the evaluation process.</p>
<p>In the book <a href="https://www.splunk.com/en_us/blog/splunklife/the-product-is-docs.html">The Product Is Docs</a>, the Splunk documentation team describes this transition as needing to be “seamless, cohesive, and logical.” Their point, much more than consistency in tone or branding, is about continuity of understanding.</p>
<p>When users move from marketing content into documentation, they are testing whether the product narrative holds up under scrutiny. If the documentation is incomplete, confusing, or disconnected from what marketing promised, confidence drops quickly, not only in the docs, but in the product itself.</p>
<p>This idea aligns closely with broader research on content-driven buying behavior. Studies on developer experience consistently show that documentation quality is a key factor in product evaluation and adoption. A report by <a href="https://www.slashdata.co/post/software-development-challenges-are-technical-and-strategic">SlashData</a> found that poor or unclear documentation is one of the top reasons developers abandon tools during evaluation, even when the underlying technology is sound.</p>
<p>What this highlights is that documentation does not sit at the end of the user journey, after a purchase has been made. It sits directly in the middle of the decision-making process. It acts as the bridge between curiosity and commitment. Marketing may introduce the problem space and position the solution, but documentation is where users validate whether the solution is real, usable, and aligned with their needs.</p>
<p>The Splunk team refers to this as creating an “integrated content experience,” where marketing and documentation work together to guide users from initial awareness through evaluation, purchase, and successful implementation.</p>
<h2 id="heading-how-to-build-trust-at-the-moment-of-highest-intent"><strong>How to Build Trust at the Moment of Highest Intent</strong></h2>
<p>When users arrive at your documentation, they’re no longer casually exploring. They’re actively evaluating whether your product fits their needs and whether it’s worth committing time, effort, or money to. This is what makes documentation a uniquely high-intent touchpoint in the user journey.</p>
<p>In marketing terms, high-intent moments are rare and valuable. They occur when users are close to making a decision and are seeking confirmation.</p>
<p>For technical products, documentation is often the primary surface where this confirmation happens. Research from <a href="https://business.google.com/uk/think/consumer-insights/the-consumer-decision-making-process/">Google on decision-making behavior</a> shows that users rely heavily on “trust signals” at moments of intent, and for technical buyers, accuracy and depth of information matter more than persuasive language.</p>
<p>This is why documentation quality has an outsized impact on outcomes. When users encounter clear and practical documentation during evaluation, confidence increases. When they encounter gaps, inconsistencies, or ambiguity, doubt sets in immediately. Unlike marketing content, documentation doesn’t get the benefit of abstraction. It’s expected to be precise and complete.</p>
<p>Documentation is part of a continuous guide that “holds a customer’s hand” from evaluation through purchase and into successful usage. This framing is important because it highlights that trust isn’t built once and forgotten. It’s maintained across stages. Documentation inherits the trust marketing creates, but it must earn its own by demonstrating technical credibility and practical value.</p>
<p>This is the point where many products fail because marketing successfully drives interest, but documentation breaks the trust loop by failing to support the claims made earlier. When that happens, users don’t just lose confidence in the docs but also in the product and, by extension, the company behind it.</p>
<h2 id="heading-so-what-actually-builds-trust-in-documentation"><strong>So, What Actually Builds Trust in Documentation?</strong></h2>
<p>Trust in documentation isn’t subjective. It’s shaped by specific, observable qualities that users quickly pick up on during evaluation.</p>
<p>Accuracy is the foundation. When examples work as described, and behavior matches documentation, users feel safe moving forward. When it doesn’t, you have a problem. Studies on <a href="https://www.archbee.com/blog/invisible-roadblock-poor-documentation-and-how-to-break-through">developer experience</a> consistently rank inaccurate documentation as one of the fastest ways to lose adoption, even when the underlying product is powerful.</p>
<p>Completeness is a signal of maturity. Documentation that covers core workflows, edge cases, and limitations tells users that the product has been thought through. Gaps, missing sections, or “coming soon” pages suggest instability or unfinished work. For evaluators, this raises red flags about long-term viability.</p>
<p>Another point is honesty, which plays an equally important role. Strong documentation doesn’t hide constraints or trade-offs. Instead, it acknowledges them clearly. Research in trust psychology shows that transparency, even about limitations, increases perceived credibility because it reduces uncertainty and surprises later.</p>
<p>Also, more recently updated docs reflect better on team health. Updated documentation signals active maintenance and ongoing investment. On the other hand, outdated docs suggest abandonment or stagnation. For technical buyers, this is often interpreted as a warning sign about support and future development.</p>
<p>Finally, usability determines whether trust can even form. Documentation that is difficult to navigate, poorly structured, or even hard to search fails before its content is evaluated. <a href="https://www.nngroup.com/articles/information-foraging/">Nielsen Norman Group’s research on information-seeking behavior</a> shows that users abandon content quickly when they cannot find answers with minimal effort, regardless of quality.</p>
<p>Together, these factors shape how users perceive not just the documentation, but the product and organization behind it.</p>
<h2 id="heading-documentation-and-marketing-a-partnership">Documentation and Marketing: A Partnership</h2>
<p>In the book The Product is Docs, the authors observe that documentation teams and marketing teams "have more in common than you might think." Both are responsible for communicating product value to audiences, both create content that influences buying decisions, and both play crucial roles in the customer journey.</p>
<p>Yet these teams often operate separately, thereby creating disconnected experiences for users.</p>
<p>Marketing promises capabilities without understanding technical constraints, while documentation focuses on implementation details without connecting to broader use cases or business value.</p>
<p>The result of this is a jarring transition from marketing to product that undermines both efforts.</p>
<h3 id="heading-so-how-can-marketing-help-documentation">So, how can marketing help documentation?</h3>
<p>Marketing teams bring a valuable perspective to documentation:</p>
<ul>
<li><p><strong>Customer insights</strong>: Marketing interacts directly with prospects and customers through demos, events, and demand generation. These interactions surface real questions and pain points that documentation should address.</p>
</li>
<li><p><strong>Competitive intelligence</strong>: Understanding how competitors document their products can reveal opportunities for differentiation or improvement.</p>
</li>
<li><p><strong>Strategic priorities</strong>: Knowing which features marketing will emphasize helps documentation teams focus their effort where it will have the most impact.</p>
</li>
<li><p><strong>Target audience definition</strong>: Marketing's customer research and personas can inform documentation structure and examples.</p>
</li>
</ul>
<h3 id="heading-andhow-can-good-documentation-help-marketing">And...how can good documentation help marketing?</h3>
<p>Documentation teams bring equally valuable assets to the partnership:</p>
<ul>
<li><p><strong>Technical accuracy</strong>: Documentation teams can review marketing materials for technical correctness and help refine messaging to be both compelling and accurate.</p>
</li>
<li><p><strong>Content leverage</strong>: Well-written documentation can be adapted into blog posts, white papers, and other marketing content.</p>
</li>
<li><p><strong>User feedback</strong>: Documentation teams often receive direct feedback from users about what's confusing or what features resonate most.</p>
</li>
<li><p><strong>Writing expertise</strong>: Technical writers can help develop clearer, more effective marketing content.</p>
</li>
</ul>
<p>The Splunk team emphasizes that this collaboration should span the entire release cycle: "Before you write" (to align on priorities and naming), "As you write" (to validate approaches), and "After you write" (to ensure consistency and quality).</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770917891627/176c277b-2ac7-4f2a-bcdd-c236c98f06bc.png" alt="Release cycle" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p><strong>Figure 1:</strong> <em>Embedding docs in the release cycle</em></p>
<h2 id="heading-practical-implications-treating-docs-as-marketing"><strong>Practical Implications: Treating Docs as Marketing</strong></h2>
<p>If documentation is indeed a marketing tool, what changes in how we approach it?</p>
<h3 id="heading-1-documentation-should-ship-with-product-launches">1. Documentation should ship with product launches</h3>
<p>This is where my own experience began. Documentation can't be an afterthought that ships weeks after launch. If docs are part of how users evaluate and adopt the product, they need to be ready when the product is. This means involving documentation teams early in the development process, not at the end.</p>
<p>As the Splunk team notes in their discussion of Agile development: "There is no definition of ‘done’ without docs. Customer documentation is part of the working software." This principle should extend to marketing launches as well.</p>
<h3 id="heading-2-invest-in-documentation-quality-and-discoverability">2. Invest in documentation quality and discoverability</h3>
<p>If documentation influences buying decisions, it deserves the same attention to quality and user experience as marketing websites. This means:</p>
<ul>
<li><p>Professional design and navigation</p>
</li>
<li><p>Fast search functionality</p>
</li>
<li><p>Clear information architecture</p>
</li>
<li><p>Mobile-friendly formatting</p>
</li>
<li><p>SEO optimization for relevant queries</p>
</li>
</ul>
<p>Too often, documentation sites are neglected from a UX perspective while marketing pages receive constant refinement. This sends a message about priorities that users notice.</p>
<h3 id="heading-3-create-an-integrated-content-experience">3. Create an integrated content experience</h3>
<p>The transition from marketing to documentation should feel natural. This requires:</p>
<ul>
<li><p><strong>Consistent terminology</strong>: Features should be named the same way in marketing materials and documentation.</p>
</li>
<li><p><strong>Aligned narratives</strong>: The use cases highlighted in marketing should have detailed implementation guides in the documentation.</p>
</li>
<li><p><strong>Cross-linking</strong>: Marketing pages should link directly to relevant documentation sections, and vice versa.</p>
</li>
<li><p><strong>Shared success metrics</strong>: Both teams should care about the same outcomes –&nbsp;not just awareness or signups, but successful implementation and adoption.</p>
</li>
</ul>
<p>The Splunk team describes this as helping customers avoid feeling "disoriented or, worse, feel misled." Good documentation delivers on marketing's promises with specifics on how to achieve the outcomes that were advertised.</p>
<h3 id="heading-4-measure-documentation-impact-on-conversion">4. Measure documentation impact on conversion</h3>
<p>If documentation is part of the marketing funnel, it should be measured as such. As a documentation team, you should track metrics like:</p>
<ul>
<li><p>Documentation page views from prospective customers</p>
</li>
<li><p>Time spent in docs before conversion</p>
</li>
<li><p>Most-viewed pages during evaluation periods</p>
</li>
<li><p>Correlation between documentation engagement and trial-to-paid conversion</p>
</li>
<li><p>Customer feedback on documentation quality during the buying process</p>
</li>
</ul>
<p>These metrics help demonstrate documentation's business impact and justify investment in quality improvements.</p>
<blockquote>
<p>If you're curious about examples of docs that embody these strategies, then you might want to check out&nbsp;<a href="https://www.twilio.com/docs">Twilio</a>,&nbsp;<a href="https://docs.stripe.com/">Stripe</a>, or, as mentioned earlier, the&nbsp;<a href="https://docs.splunk.com/Documentation">Splunk</a>&nbsp;docs.</p>
</blockquote>
<h3 id="heading-why-does-all-this-matter-now">Why does all this matter now?</h3>
<p>The role of documentation in driving adoption has always been important, but several trends are making it more critical:</p>
<p>First, as more companies adopt PLG strategies, the product experience, including documentation, becomes the primary sales vehicle. There's no sales team to answer questions or guide implementation. The docs must do that work.</p>
<p>Second, technical buyers increasingly want to evaluate products independently before engaging with sales. Documentation is often their primary evaluation tool.</p>
<p>We’re also seeing shorter sales cycles. When buying cycles compress, there's less time for demos and sales engineering. Documentation needs to answer questions immediately.</p>
<p>And finally, for developer tools and infrastructure products, individual contributors often drive adoption from the bottom up. These users rely heavily on documentation for their evaluation process.</p>
<p>In this environment, treating documentation as an operational necessity rather than a strategic asset is a competitive disadvantage. Companies that recognize documentation as a core part of their go-to-market strategy and invest accordingly see better conversion rates and stronger customer relationships.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Documentation is not just a post-purchase support resource. It's an active participant in the customer journey, influencing buying decisions and driving adoption.</p>
<p>For technical products, especially, documentation often carries more weight than traditional marketing materials.</p>
<p>This doesn't mean documentation should become marketing copy. It means recognizing that documentation performs a marketing function such as communicating value and building credibility, even while maintaining its technical accuracy and practical focus.</p>
<p>As the Splunk documentation team wisely notes, the goal is to create an integrated content experience where marketing and documentation work together to guide users from initial interest through successful implementation.</p>
<p>So, the requirement that documentation be ready at release time wasn't just operational hygiene. It was recognition that our product wasn't truly ready for market until users could both evaluate it and successfully use it. That's what makes documentation a marketing tool, not because it makes exaggerated claims, but because it delivers on the promise that marketing makes.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use to Docker with Node.js: A Handbook for Developers ]]>
                </title>
                <description>
                    <![CDATA[ In this handbook, you’ll learn what Docker is and why it’s a must-have skill for backend and full-stack developers. And, most importantly, you’ll learn how to use it in real-world projects from start to finish. We will go far beyond the usual “Hello ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-to-docker-with-nodejs-handbook/</link>
                <guid isPermaLink="false">691cf09fea147a95b92d3551</guid>
                
                    <category>
                        <![CDATA[ Docker ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Devops ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ci-cd ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Technical writing  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ handbook ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Oghenekparobo Stephen ]]>
                </dc:creator>
                <pubDate>Tue, 18 Nov 2025 22:18:07 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763502750050/74610cbc-124b-48aa-9cb6-7ed861123511.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this handbook, you’ll learn what Docker is and why it’s a must-have skill for backend and full-stack developers. And, most importantly, you’ll learn how to use it in real-world projects from start to finish.</p>
<p>We will go far beyond the usual “Hello World” examples and walk you through containerizing a complete full-stack JavaScript application (Node.js + Express backend, HTML/CSS/JS frontend, MongoDB database, and Mongo Express admin UI).</p>
<p>You’ll learn about networking multiple containers, orchestrating everything with Docker Compose, building and versioning your own images, persisting data with volumes, and securely pushing your Images to a private AWS ECR repository for sharing and production deployment.</p>
<p>By the end, you’ll be able to eliminate “it works on my machine” issues, confidently manage multi-service applications, deploy consistent environments anywhere, and integrate Docker into your daily workflow and CI/CD pipelines like a pro.</p>
<p>Since Docker is such a key skill for backend developers, we’ll start by covering its basic concepts.</p>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>This technical handbook is designed for developers who have some practical, hands-on experience in full-stack development. You should be comfortable deploying applications and have a basic understanding of CI/CD pipelines.</p>
<p>While we’ll cover Docker from the ground up, this guide is not for absolute beginner developers. I assume you have real-world development experience and want to level up your workflow with Docker.</p>
<p>Finally, a basic familiarity with AWS and general deployment concepts will also be useful, though you don’t need to be an expert. This handbook is ideal for developers looking to enhance their production-grade skills and confidently integrate Docker into their projects.</p>
<h2 id="heading-table-of-contents">Table of Contents:</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-what-is-a-container">What is a Container?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-docker-vs-virtual-machines">Docker vs Virtual Machines</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-docker-installation">Docker Installation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-basic-docker-commands">Basic Docker Commands</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-practice-with-javascript">Practice with JavaScript</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-how-to-pull-the-mongodb-image">How to Pull the MongoDB Image</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-pull-the-mongo-express-image">How to Pull the Mongo Express Image</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-docker-network">Docker Network</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-run-the-mongo-container">How to Run the Mongo Container</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-run-the-mongo-express-container">How to Run the Mongo Express Container</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-connect-nodejs-to-mongodb">How to Connect Node.js to MongoDB</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-docker-compose">How to Use Docker Compose</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-why-use-docker-compose">Why Use Docker Compose?</a></li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-our-own-docker-image">How to Build Our Own Docker Image</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-the-solution">The Solution</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-mongodb-works">Why Mongodb Works</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-add-your-app-to-docker-compose">Add Your App to Docker Compose</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-start-all-services">Start All Services</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-verify-everything-works">Verify Everything Works</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-changed-and-why-it-works">What Changed and Why It Works</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-manage-your-containers">How to Manage Your Containers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-a-private-docker-repository">How to Create a Private Docker Repository</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-step-1-get-your-aws-access-keys">Step 1: Get Your AWS Access Keys</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-check-if-aws-cli-is-installed">Step 2: Check if AWS CLI is Installed</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-configure-aws-cli">Step 3: Configure AWS CLI</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-4-test-your-aws-configuration">Step 4: Test Your AWS Configuration</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-5-login-to-ecr-docker-registry">Step 5: Login to ECR (Docker Registry)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-understanding-image-naming-in-docker-repositories">Understanding Image Naming in Docker Repositories</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-6-build-tag-and-push-your-image">Step 6: Build, Tag, and Push Your Image</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-assignment-create-and-push-a-new-version">Assignment: Create and Push a New Version</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-deploying-our-image">Deploying Our Image</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-must-we-use-the-full-image-url-for-ecr">Why Must We Use the Full Image URL for ECR</a>?</p>
</li>
<li><p><a class="post-section-overview" href="#heading-deploy-your-app-using-docker-compose">Deploy Your App Using Docker Compose</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-sharing-our-private-docker-image">Sharing Our Private Docker Image</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-docker-volumes">Docker Volumes</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-how-docker-volumes-work">How Docker Volumes Work</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-types-of-docker-volumes">Types of Docker Volumes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-example-docker-compose-file-using-volumes">Example Docker Compose File Using Volumes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-start-your-application">Start Your Application</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-what-is-a-container">What is a Container?</h2>
<p>A container is a way to package an application together with everything it needs, including its dependencies, libraries, and configuration files.</p>
<p>Because containers are portable, they can be shared across teams and deployed on any machine without worrying about compatibility.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762863191484/827d0731-a392-419f-b17b-9a3611a4f3b4.jpeg" alt="pictures of stack containers, to portrait or give an idea what containers are or a vivid pictureof containers aliking to containers in docker" class="image--center mx-auto" width="736" height="736" loading="lazy"></p>
<h3 id="heading-where-do-containers-live">Where Do Containers Live?</h3>
<p>Since containers are portable and can be shared across teams and systems, they need a place to live. That’s where container repositories come in – special storage locations for containers. Organizations can have private repositories for internal use, while public ones like <a target="_blank" href="https://hub.docker.com/">Docker Hub</a> let anyone browse and use shared containers.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762863680430/caddd581-08e1-45c7-a676-818ad364f56b.png" alt="an image of docker hub, showing a catalogue of images" class="image--center mx-auto" width="2874" height="1718" loading="lazy"></p>
<p>If you visit the catalog page on Docker Hub, you will see a variety of container repositories, both official and community-made, from developers and teams like Redis, Jenkins, and many others.</p>
<p>In the past, when multiple developers worked on different projects, each had to manually install services on their own systems. Since different developers often use different operating systems like Linux, macOS, and Windows, the setup process was never the same. It took a lot of time, led to plenty of errors, and made setting up new environments a real headache, especially when you had to repeat it for multiple services.</p>
<p>Docker changed the game for developers and teams. Instead of manually installing every service and dependency, you can just run a single Docker command to start a container. Each container has its own isolated environment with everything it needs, so it runs the same on any machine, no matter if it’s Windows, macOS, or Linux. This makes collaboration smoother and eliminates all the bottlenecks that come from different setups, missing dependencies, or version mismatches.</p>
<p>In short, Docker is a platform that packages your app and its dependencies into a single, portable container, so it runs the same way everywhere.</p>
<h2 id="heading-docker-vs-virtual-machines">Docker vs Virtual Machines</h2>
<p>Docker and virtual machines (VMs) are both ways to run apps in a “virtual” environment, but they work differently. To understand the differences, it helps to know a bit about how computers run software.</p>
<p>A quick look at the layers:</p>
<ul>
<li><p><strong>Kernel:</strong> This is the part of the operating system that talks to your computer’s hardware, like the CPU, memory, and disk. Think of it as the middleman between your apps and your computer.</p>
</li>
<li><p><strong>Application layer:</strong> This is where programs and apps run. It sits on top of the kernel and uses it to access hardware resources.</p>
</li>
</ul>
<p>So, now let’s get into a bit more detail about Virtual Machines. A VM virtualizes the <strong>entire operating system</strong>, which means it comes with its own kernel and its own application layer. When you download a VM, you are basically getting a full OS inside your computer, often several gigabytes in size.</p>
<p>Because it has to boot its own OS, VMs start slowly. But VMs are very compatible, and can run on almost any host because they include everything they need.</p>
<p>Docker, on the other hand, only virtualizes the <strong>application layer</strong>, not the full OS. Containers share the host system’s kernel but include everything the app needs, dependencies, libraries, and configuration.</p>
<p>Docker images are small, often just a few megabytes. Containers start almost instantly because they don’t boot a full OS. A Docker container can run anywhere Docker is installed, no matter what operating system your computer uses.</p>
<p>In simple terms, to summarize:</p>
<ul>
<li><p>A VM is like running a whole computer inside your computer – big, heavy, and slow.</p>
</li>
<li><p>A Docker container is like a self-contained app package – small, fast, and portable.</p>
</li>
</ul>
<p>Here’s a quick comparison:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Feature</td><td>Virtual Machine</td><td>Docker Container</td></tr>
</thead>
<tbody>
<tr>
<td>Size</td><td>GBs (large)</td><td>MBs (small)</td></tr>
<tr>
<td>Startup Speed</td><td>Slow</td><td>Fast</td></tr>
<tr>
<td>OS Layer</td><td>Full OS + kernel</td><td>Shares host kernel</td></tr>
<tr>
<td>Portability</td><td>Runs on compatible host</td><td>Runs anywhere Docker is installed</td></tr>
</tbody>
</table>
</div><h2 id="heading-docker-installation">Docker Installation</h2>
<p>Alright, now that you know what Docker is, let’s get it running on your own machine.</p>
<p>Docker works on Windows, macOS, and Linux, but each system has slightly different steps. The official Docker <a target="_blank" href="https://docs.docker.com/get-started/introduction/">documentation</a> has clear instructions for all operating systems under Docker Docs: Install Docker.</p>
<p>If you are more of a visual learner, this YouTube video walks you through installing Docker on Windows and Linux step by step: <a target="_blank" href="https://www.youtube.com/watch?v=BuGEGM_elXY">Watch here</a>.</p>
<p>Here is a simple roadmap:</p>
<p>First, check your system requirements. Docker won’t run on every computer, so make sure your OS version is supported (the official <a target="_blank" href="https://docs.docker.com/engine/install/">docs</a> have a checklist).</p>
<ol>
<li><p>Windows and macOS users:</p>
<ul>
<li><p><strong>Newer systems:</strong> Download and install <a target="_blank" href="https://docs.docker.com/desktop/"><strong>Docker Desktop</strong></a><strong>.</strong> It’s the easiest way to get started.</p>
</li>
<li><p><strong>Older systems:</strong> If your computer doesn’t support Docker Desktop (for example, missing Hyper-V or older OS versions), you can use <a target="_blank" href="https://docker-docs.uclv.cu/toolbox/toolbox_install_windows/"><strong>Docker Toolbox</strong></a>. Toolbox installs Docker using a lightweight virtual machine, so you can still run containers even on older machines.</p>
</li>
</ul>
</li>
<li><p>Linux users: You will usually install Docker through your package manager (<code>apt</code> for Ubuntu/Debian, <code>yum</code> for CentOS/Fedora, etc.). The official <a target="_blank" href="https://docs.docker.com/desktop/setup/install/linux/">docs</a> show the commands for your distro.</p>
</li>
</ol>
<p>Then verify your installation: Open a terminal or command prompt and type:</p>
<pre><code class="lang-bash">docker --version
</code></pre>
<p>If you see the Docker version displayed, congratulations! Docker is ready to go.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762871221981/6b01cf18-a8b5-4aa9-b213-38cffd4ae5f4.png" alt="docker version displayed on cli" class="image--center mx-auto" width="632" height="34" loading="lazy"></p>
<p>Once Docker is installed, you’ll be ready to start running containers, pulling images, and experimenting with your apps in a safe, isolated environment.</p>
<p><strong>Tip for beginners:</strong></p>
<p>If you’re on an older machine and using Docker Toolbox, commands are mostly the same, but you will run them inside the <strong>Docker Quickstart Terminal</strong>, which sets up the virtual machine for you.</p>
<h2 id="heading-basic-docker-commands">Basic Docker Commands</h2>
<p>So far, we have been throwing around terms like images and containers, sometimes even interchangeably. But there is an important difference:</p>
<ul>
<li><p><strong>Docker image:</strong> Think of an image as a <strong>blueprint</strong> or a package. It contains everything your app needs: the code, libraries, dependencies, and configuration, but it’s not running yet.</p>
</li>
<li><p><strong>Docker container:</strong> A container is a <strong>running instance of an image</strong>. When you start a container, Docker takes the image and runs it in its own isolated environment.</p>
</li>
</ul>
<p>A helpful way to remember it is this: the image is the recipe, while the container is the cake**.** You can have one recipe (image) and make multiple cakes (containers) from it.</p>
<p><strong>Important note:</strong> Docker Hub stores images, not containers. So when you pull something from Docker Hub, you’re downloading an image. For example:</p>
<pre><code class="lang-bash">docker pull redis
</code></pre>
<p>Here’s what you’ll see:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762872367018/39039261-9617-4e5f-8156-9529697d0667.png" alt="docker run redis shown on cli" class="image--center mx-auto" width="1130" height="410" loading="lazy"></p>
<p>This command downloads the Redis image to your machine. Once the download is complete, you can see all the images you have locally with:</p>
<pre><code class="lang-bash">docker images
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762872440545/bfdb401f-7dd6-4f72-920b-545fbf5193e1.png" alt="running docker images on cli" class="image--center mx-auto" width="1654" height="880" loading="lazy"></p>
<p>From there, you can start a container from an image whenever you need it:</p>
<pre><code class="lang-bash">docker run -d --name my-redis redis
</code></pre>
<p>This command starts a container, <code>my-redis</code>, from the <code>redis</code> image you just pulled.</p>
<ul>
<li><p><code>docker run</code> tells Docker to start a new container from an image.</p>
</li>
<li><p><code>-d</code> stands for “detached mode.” It means the container runs in the background so you can keep using your terminal.</p>
</li>
<li><p><code>--name my-redis</code> gives your container a friendly name (<code>my-redis</code>) instead of letting Docker assign a random one. It makes it easier to manage later.</p>
</li>
<li><p><code>redis</code> is the image you are using to start the container.</p>
</li>
</ul>
<p>To see all containers that are currently running, you can use:</p>
<pre><code class="lang-bash">docker ps
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762873018123/2e184c25-e4f1-445c-b182-81987929c014.png" alt="ran docker ps in the terminal to list all running containers" class="image--center mx-auto" width="1518" height="396" loading="lazy"></p>
<p>This will list containers with details like:</p>
<ul>
<li><p>Container ID</p>
</li>
<li><p>Name</p>
</li>
<li><p>Status (running or stopped)</p>
</li>
<li><p>The image it’s running from</p>
</li>
</ul>
<p>If you want to see all containers, even ones that aren’t running, you can add the <code>-a</code> flag:</p>
<pre><code class="lang-bash">docker ps -a
</code></pre>
<h3 id="heading-how-to-specify-a-version-of-an-image">How to Specify a Version of an Image:</h3>
<p>By default, Docker pulls the <strong>latest version</strong> of an image. But sometimes you might need a specific version. You can do this using a colon (<code>:</code>) followed by the version tag. For example:</p>
<pre><code class="lang-bash">docker pull redis:7.2
docker run -d --name my-redis redis:7.2
</code></pre>
<p>To know which versions are available, you can visit <a target="_blank" href="https://hub.docker.com/repositories"><strong>Docker Hub</strong></a> or check the image tags online. Also, running <code>docker images</code> on your machine will show you all downloaded images and their versions.</p>
<h3 id="heading-how-to-stop-start-and-remove-a-container">How to Stop, Start, and Remove a Container</h3>
<p>If you want to stop a running container, run this:</p>
<pre><code class="lang-bash">docker stop my-redis
</code></pre>
<p>To start it again:</p>
<pre><code class="lang-bash">docker start my-redis
</code></pre>
<p>You can also <strong>remove a container</strong> if you no longer need it:</p>
<pre><code class="lang-bash">docker rm my-redis
</code></pre>
<h3 id="heading-how-to-restart-a-container">How to Restart a Container</h3>
<p>You can restart a container using its <strong>container ID</strong> (or name) if something crashes, needs a refresh, or you just want to apply changes.</p>
<p>For example:</p>
<pre><code class="lang-bash">docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS      NAMES
c002bed0ae9a   redis     <span class="hljs-string">"docker-entrypoint.s…"</span>   3 minutes ago   Up 3 minutes   6379/tcp   my-redis
</code></pre>
<p>Restart it like this:</p>
<pre><code class="lang-bash">docker restart c002bed0ae9a
</code></pre>
<p>or by name:</p>
<pre><code class="lang-bash">docker restart my-redis
</code></pre>
<p>Other handy ways:</p>
<ul>
<li><p><strong>Stop then start</strong></p>
<pre><code class="lang-bash">  docker stop c002bed0ae9a
  docker start c002bed0ae9a
</code></pre>
</li>
<li><p><strong>Start with logs</strong></p>
<pre><code class="lang-bash">  docker start c002bed0ae9a &amp;&amp; docker logs -f c002bed0ae9a
</code></pre>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762873445952/ffb56b5d-f850-4b53-998d-467ed431a191.png" alt="starting a docker container with logs" class="image--center mx-auto" width="2880" height="1362" loading="lazy"></p>
<h3 id="heading-how-to-run-multiple-redis-containers-and-understanding-ports">How to Run Multiple Redis Containers and Understanding Ports</h3>
<p>Right now, you have a Redis container running:</p>
<pre><code class="lang-bash">docker ps
</code></pre>
<p>It shows something like this:</p>
<pre><code class="lang-bash">CONTAINER ID   IMAGE     COMMAND                  STATUS          PORTS      NAMES
c002bed0ae9a   redis     <span class="hljs-string">"docker-entrypoint.s…"</span>   Up 20 minutes   6379/tcp   my-redis
</code></pre>
<p>Notice the <strong>PORTS</strong> column: <code>6379/tcp</code>. This means the container is running Redis on its internal port 6379. By default, this port is inside the container and is not automatically exposed to your computer (the host). Docker maps it only if you specify it.</p>
<h4 id="heading-trying-to-run-another-redis-container-on-the-same-port">Trying to Run Another Redis Container on the Same Port</h4>
<p>If you try:</p>
<pre><code class="lang-bash">docker run -d --name my-redis2 redis:7.4.7-alpine
</code></pre>
<p>It will fail to map the host port 6379 because the first container is already using it. This is where port binding comes in.</p>
<h4 id="heading-what-is-port-binding">What is Port Binding?</h4>
<p>Port binding (also called port mapping) is the mechanism Docker uses to connect a port inside a container to a port on your host machine (your laptop/desktop/server).</p>
<p>Without port binding, any service running inside a container is completely isolated: it can listen on its internal ports (for example, Redis on 6379, a Node.js app on 3000, MongoDB on 27017), but nothing outside the container, including your browser, another app on your computer, or even another container on a different network, can reach it.</p>
<ul>
<li><p><strong>Container Port</strong>: The port inside the container where the app is running (Redis defaults to <code>6379</code>).</p>
</li>
<li><p><strong>Host Port</strong>: The port on your computer that you want to use to access that container.</p>
</li>
</ul>
<p>Docker lets you map a container port to a different host port using the <code>-p</code> flag.</p>
<h4 id="heading-running-a-second-redis-container-on-a-different-host-port">Running a Second Redis Container on a Different Host Port</h4>
<pre><code class="lang-bash">docker run -d --name my-redis2 -p 6380:6379 redis:7.4.7-alpine
</code></pre>
<p><code>-p 6380:6379</code> maps host port 6380 to container port 6379.</p>
<ul>
<li><p>Now you can connect to Redis in the second container using <code>localhost:6380</code>.</p>
</li>
<li><p>Inside the container, Redis still runs on port 6379.</p>
</li>
</ul>
<p>Check both containers:</p>
<pre><code class="lang-bash">docker ps
</code></pre>
<p>Output will look like this:</p>
<pre><code class="lang-bash">CONTAINER ID   IMAGE     STATUS          PORTS             NAMES
c002bed0ae9a   redis     Up 20 minutes   6379/tcp          my-redis
d123abcd5678   redis     Up 1 minute     0.0.0.0:6380-&gt;6379/tcp   my-redis2
</code></pre>
<p>The first container is running internally on 6379 (host port not exposed), while the second container is mapped so host port 6380 forwards traffic to container port 6379.</p>
<p>Think of each container as a room with a phone line (container port).</p>
<ul>
<li><p>You want to call that room from the outside (host).</p>
</li>
<li><p>You can’t use the same external phone line for two rooms at the same time.</p>
</li>
<li><p>With <strong>port binding</strong>, you assign a different external line for each room, even if the internal phone number is the same.</p>
</li>
</ul>
<h4 id="heading-why-port-binding-exists">Why Port Binding Exists</h4>
<ol>
<li><p><strong>Avoid port conflicts on the host:</strong> Only one process on your computer can use a given port at a time. If you already have one Redis container using host port 6379, a second container cannot also bind to the same host port. Port binding lets you run many identical containers side-by-side by mapping each one to a different host port (6379 → 6380, 6381, etc.).</p>
</li>
<li><p><strong>Access containerised services from your host:</strong> Your browser, Postman, MongoDB Compass, redis-cli, curl, etc., all run on the host. Without -p, they have no way to talk to services inside containers.</p>
</li>
<li><p><strong>Selective exposure:</strong> You don’t have to expose every port a container uses. Only map the ports you actually need externally, keeping the rest private and secure.</p>
</li>
</ol>
<p>It also gives you more flexibility in development and production. In development, you might map container 3000 to host 3000. But in production (for example, behind a reverse proxy), you might map container 3000 to host 80 or 443, or not expose it at all and let another container talk to it over Docker’s internal network.</p>
<h3 id="heading-how-to-explore-a-container">How to Explore a Container</h3>
<p>To explore a container, run:</p>
<pre><code class="lang-bash">docker <span class="hljs-built_in">exec</span> -it my-redis2 /bin/sh
</code></pre>
<ul>
<li><p><code>docker exec</code> runs a command in the container.</p>
</li>
<li><p><code>-it</code> interactive terminal (lets you type and see output).</p>
</li>
<li><p><code>/bin/sh</code> starts a shell inside the container.</p>
</li>
</ul>
<p>Once inside, your prompt changes to something like:</p>
<pre><code class="lang-bash">/data <span class="hljs-comment">#</span>
</code></pre>
<p>Now you can <strong>list files</strong>, navigate directories, or run programs, all inside the container, without affecting your host machine.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762876729378/d16e8f00-ab9c-447b-b274-76d613b30ce3.png" alt="result of running docker exec -it my-redis2 /bin/sh" class="image--center mx-auto" width="2880" height="1800" loading="lazy"></p>
<h3 id="heading-docker-run-vs-docker-start"><code>docker run</code> vs <code>docker start</code></h3>
<p>We have been using <code>docker run</code> and <code>docker start</code> throughout this article, but here’s why the difference is important:</p>
<ul>
<li><p><strong>Avoid accidental duplicates:</strong> Using <code>docker run</code> every time creates a new container. If you just want to restart something you already set up, <code>docker start</code> is faster and safer.</p>
</li>
<li><p><strong>Maintain configuration:</strong> <code>docker start</code> preserves the container’s original settings, ports, volumes, and names so you don’t risk breaking anything by changing options.</p>
</li>
<li><p><strong>Work efficiently with multiple containers:</strong> When running multiple services or different versions of the same app, knowing when to <code>run</code> vs <code>start</code> helps you manage resources, avoid port conflicts, and keep your workflow smooth.</p>
</li>
<li><p><strong>Speed up your workflow:</strong> Starting existing containers is almost instant, while creating a new one takes slightly longer.</p>
</li>
</ul>
<p><strong>Bottom line</strong> <code>docker run</code> = create something new, while <code>docker start</code> = resume what you already have.</p>
<h2 id="heading-practice-with-javascript">Practice with JavaScript</h2>
<p>Now that we have covered the core Docker concepts, let’s put them into action. In this section, we’ll containerize a simple JavaScript project that consists of:</p>
<ul>
<li><p><strong>A frontend:</strong> Built with HTML, CSS, and JavaScript</p>
</li>
<li><p><strong>A backend:</strong> A simple Node.js server (<code>server.js</code>)</p>
</li>
<li><p><strong>A database:</strong> A MongoDB instance pulled directly from Docker Hub</p>
</li>
<li><p><strong>A UI for MongoDB:</strong> Using <strong>Mongo Express</strong> to visualize and manage our database</p>
</li>
</ul>
<p>This example demonstrates how Docker can manage multiple components of an application, including code, dependencies, and services in isolated, consistent environments.</p>
<p>You can <a target="_blank" href="https://github.com/Oghenekparobo/docker_tut_js">pull the starter project from GitHub here</a>.</p>
<p>Or clone it directly using your terminal:</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/Oghenekparobo/docker_tut_js.git
<span class="hljs-built_in">cd</span> docker_tut_js
</code></pre>
<p>This contains the basic HTML and JavaScript files along with the Node.js backend.</p>
<p>Next, we will prepare to set up our database. Head over to <a target="_blank" href="https://hub.docker.com/">Docker Hub</a> and type <strong>“mongo”</strong> in the search box. You will see the official MongoDB image published by Docker.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762950041097/89f54e21-f607-488a-8d98-d688733270c4.png" alt="official mongo db database in dockerhub" class="image--center mx-auto" width="2880" height="1504" loading="lazy"></p>
<h3 id="heading-how-to-pull-the-mongodb-image">How to Pull the MongoDB Image</h3>
<p>Now that you have explored the official MongoDB image on Docker Hub, let’s actually pull it into your local environment.</p>
<p>Open your terminal, navigate to your project directory (for example, <code>docker_tut_js</code>), and run:</p>
<pre><code class="lang-bash">docker pull mongo
</code></pre>
<p>This command tells Docker to download the latest version of the MongoDB image from Docker Hub.</p>
<p>You will see output similar to this:</p>
<pre><code class="lang-bash">Using default tag: latest
latest: Pulling from library/mongo
b8a35db46e38: Already exists 
a637dbfff7e5: Pull complete 
0c9047ace63c: Pull complete 
02cd4cf70021: Pull complete 
dfb5d357a025: Pull complete 
007bf0024f67: Pull complete 
67fd8af3998d: Pull complete 
d702312e8109: Pull complete 
Digest: sha256:7d1a1a613b41523172dc2b1b02c706bc56cee64144ccd6205b1b38703c85bf61
Status: Downloaded newer image <span class="hljs-keyword">for</span> mongo:latest
docker.io/library/mongo:latest
</code></pre>
<p>Here’s what’s happening:</p>
<ul>
<li><p><strong>“Using default tag: latest”</strong>: Docker pulls the most recent version of MongoDB since no specific version was provided.</p>
</li>
<li><p><strong>“Pulling from library/mongo”</strong>: It’s downloading from Docker’s official image library.</p>
</li>
<li><p><strong>“Pull complete”</strong>: Each line represents a layer of the image being successfully downloaded.</p>
</li>
<li><p><strong>“Downloaded newer image for mongo:latest”</strong>: Confirms that the MongoDB image is now stored locally on your system.</p>
</li>
</ul>
<p>You can confirm that it’s available by running:</p>
<pre><code class="lang-bash">docker images
</code></pre>
<p>You should see <strong>mongo</strong> listed in the repository column.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762950246712/343d4d23-9c61-4480-956c-a5c2cd391889.png" alt="mongo db listed in the repository column after running docker images" class="image--center mx-auto" width="1164" height="740" loading="lazy"></p>
<h3 id="heading-how-to-pull-the-mongo-express-image">How to Pull the Mongo Express Image</h3>
<p>Now that the MongoDB image is ready, let’s pull the <strong>Mongo Express</strong> image.</p>
<p>Mongo Express is a lightweight web-based interface that lets you view and manage your MongoDB collections through a browser, similar to how phpMyAdmin works for MySQL.</p>
<p>Open your terminal (still in your project directory) and run:</p>
<pre><code class="lang-bash">docker pull mongo-express
</code></pre>
<p>You’ll see output similar to this:</p>
<pre><code class="lang-bash">Using default tag: latest
latest: Pulling from library/mongo-express
b8a35db46e38: Already exists
a637dbfff7e5: Pull complete
4e0e0977e9c3: Pull complete
02cd4cf70021: Pull complete
Digest: sha256:3d6dbac587ad91d0e2eab83f09a5b31a1c8f9d91a8825ddaa6c7453c25cb4812
Status: Downloaded newer image <span class="hljs-keyword">for</span> mongo-express:latest
docker.io/library/mongo-express:latest
</code></pre>
<p>Here’s what this means:</p>
<ul>
<li><p><code>docker pull mongo-express</code> downloads the official Mongo Express image from Docker Hub.</p>
</li>
<li><p>Each <strong>“Pull complete”</strong> line represents a successfully downloaded layer of the image.</p>
</li>
<li><p><code>mongo-express:latest</code> confirms that the latest version is now stored locally.</p>
</li>
</ul>
<p>To verify that both images are available, run:</p>
<pre><code class="lang-bash">docker images
</code></pre>
<p>You should see mongo and mongo-express listed in the output.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762951088081/06345eb1-80c9-4fcd-8585-5ff309ed2779.png" alt="docker images command showing both mongo db database and mongo express images verifying they have been installed by docker on your project" class="image--center mx-auto" width="1164" height="740" loading="lazy"></p>
<p>Now that both images are downloaded, the next step is to run the containers to make sure MongoDB is up and accessible, and then connect it to Mongo Express so we can manage it through the browser.</p>
<p>Before we do that, let’s briefly look at how these two containers will communicate.</p>
<h3 id="heading-docker-network">Docker Network</h3>
<p>When MongoDB and Mongo Express run in separate containers, they need a way to talk to each other. Docker handles this using something called a <strong>Docker Network,</strong> a virtual bridge that lets containers communicate securely without exposing internal ports to the outside world.</p>
<p>When you run containers in Docker, it automatically creates an isolated network for them. Think of it like a private space where your containers can talk to each other safely without exposing everything to the outside world.</p>
<p>For example, if our MongoDB container and Mongo Express container are on the same Docker network, they can communicate just by using their container names (like <code>mongo</code> or <code>mongo-express</code>). You don’t need to use <code>localhost</code> or port numbers, as Docker handles that part internally.</p>
<p>But anything outside the Docker network (like your host machine or a Node.js app) connects through the exposed ports.</p>
<p>So later, when we package our entire application, the Node.js backend, MongoDB, Mongo Express, and even the frontend (<code>index.html</code>) into Docker, all these containers will interact smoothly through the Docker network. The browser on your computer will then connect to your Node.js app using the host address and port we have exposed.</p>
<p>By default, Docker already provides a few built-in networks. You can see them by running:</p>
<pre><code class="lang-bash">docker network ls
</code></pre>
<p>You will get something like this:</p>
<pre><code class="lang-bash">NETWORK ID     NAME      DRIVER    SCOPE
712a7144f1a0   bridge    bridge    <span class="hljs-built_in">local</span>
4ae27eedea5b   host      host      <span class="hljs-built_in">local</span>
4806000201ce   none      null      <span class="hljs-built_in">local</span>
</code></pre>
<p>These are automatically created by Docker. You don’t need to worry too much about them right now – we will just focus on creating our own custom network.</p>
<p>For our setup, we will create a separate network that both MongoDB and Mongo Express can share. Let’s call it mongo-network:</p>
<pre><code class="lang-bash">docker network create mongo-network
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762953968310/bdf51a4e-1986-48a4-922b-6f312ff99414.png" alt="mongo-network created with docker network create mongo-network then to see it in the list run docker network ls" class="image--center mx-auto" width="1144" height="740" loading="lazy"></p>
<h2 id="heading-how-to-run-the-mongo-container">How to Run the Mongo Container</h2>
<p>To make sure our MongoDB and Mongo Express containers can communicate, we need to run them inside the same Docker network. That’s why we created mongo-network earlier.</p>
<p>Let’s start with MongoDB. Remember, the <code>docker run</code> command is used to start a container from an image. In this case, we will run the official MongoDB image and attach it to our network.</p>
<p>We will also expose the default MongoDB port 27017 so it’s accessible from outside the container, and set up environment variables for the root username and password.</p>
<p>Here is the command:</p>
<pre><code class="lang-bash">docker run -p 27017:27017 -d \
  -e MONGO_INITDB_ROOT_USERNAME=admin \
  -e MONGO_INITDB_ROOT_PASSWORD=password \
  --name mongo \
  --network mongo-network \
  mongo
</code></pre>
<p>Here’s what each part does:</p>
<ul>
<li><p><code>-p 27017:27017</code> maps the container’s MongoDB port to your host machine.</p>
</li>
<li><p><code>-d</code> runs the container in detached mode (in the background).</p>
</li>
<li><p><code>-e</code> sets environment variables for the database’s root credentials.</p>
</li>
<li><p><code>--name mongo</code> gives the container a custom name for easier reference.</p>
</li>
<li><p><code>--network mongo-network</code> connects the container to the network we created.</p>
</li>
</ul>
<p>Once it runs successfully, your MongoDB instance will be up and running inside the Docker network, ready for other containers like Mongo Express to connect to it.</p>
<p>After creating your MongoDB container, you can easily check if it’s running and healthy.</p>
<p>First, run <code>docker ps</code> to see all active containers. You should see your MongoDB container (<code>mongo</code>) listed with its port <code>27017</code> exposed. To get more details about what’s happening inside the container, you can check its logs using <code>docker logs mongo</code> or, if you prefer, by using the container ID (for example: <code>docker logs 7abb38175ae28</code>). The logs will show startup messages from MongoDB, and you should look for lines indicating that the database started successfully and is ready to accept connections.</p>
<p>This is a quick way to verify that everything is working correctly before connecting other services, like Mongo Express, to it.</p>
<pre><code class="lang-bash">docker ps
</code></pre>
<p>This will list all <strong>running containers</strong>. You should see your MongoDB container (<code>mongo</code>) with its port <code>27017</code> exposed.</p>
<pre><code class="lang-bash">docker logs mongo or the id of the container e.g docker logs 7abb38175ae283429354609866c8d97521f37b535c475ae448295f8fc0ed947f
</code></pre>
<p>This will show startup messages. Look for lines indicating MongoDB started successfully and is ready to accept connections.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762956236708/44dbe331-b736-4526-8dae-019150b618d8.png" alt="checking if the mongo container is running" class="image--center mx-auto" width="2504" height="222" loading="lazy"></p>
<h2 id="heading-how-to-run-the-mongo-express-container">How to Run the Mongo Express Container</h2>
<p>Now that MongoDB is up and running, we can run Mongo Express, which is a web-based interface to manage and view your MongoDB databases. We will connect it to the same network (<code>mongo-network</code>) so it can communicate with MongoDB.</p>
<p>Here’s the command:</p>
<pre><code class="lang-bash">docker run -d \
  -e ME_CONFIG_MONGODB_ADMINUSERNAME=admin \
  -e ME_CONFIG_MONGODB_ADMINPASSWORD=password \
  -e ME_CONFIG_MONGODB_SERVER=mongo \
  --name mongo-express \
  --network mongo-network \
  -p 8081:8081 \
  mongo-express
</code></pre>
<p>Here’s what each part does:</p>
<ul>
<li><p><code>-d</code> runs the container in detached mode (in the background).</p>
</li>
<li><p><code>-e ME_CONFIG_MONGODB_ADMINUSERNAME=admin</code> sets the MongoDB admin username for Mongo Express to use.</p>
</li>
<li><p><code>-e ME_CONFIG_MONGODB_ADMINPASSWORD=password</code> sets the corresponding MongoDB password.</p>
</li>
<li><p><code>-e ME_CONFIG_MONGODB_SERVER=mongo</code> tells Mongo Express which MongoDB server to connect to. Here we use the container name <code>mongo</code> because both containers are on the same network.</p>
</li>
<li><p><code>--name mongo-express</code> gives the container a friendly name for easier reference.</p>
</li>
<li><p><code>--network mongo-network</code> connects the container to the same Docker network as MongoDB so they can talk to each other.</p>
</li>
<li><p><code>-p 8081:8081</code> exposes the Mongo Express web interface on port <code>8081</code> of your host machine.</p>
</li>
<li><p><code>mongo-express</code> the name of the Docker image we’re running.</p>
</li>
</ul>
<p>Once the container is running, you can open your browser and visit <code>http://localhost:8081</code> to access Mongo Express and interact with your MongoDB instance.</p>
<p>For more details about the available environment variables and options, you can check the official Docker Hub page for Mongo Express <a target="_blank" href="https://hub.docker.com/_/mongo-express">here</a>.</p>
<p>Before opening your browser at <a target="_blank" href="http://localhost:8081"><code>http://localhost:8081</code></a>, it’s a good idea to check if the Mongo Express container is running properly. You can do this by viewing its logs:</p>
<pre><code class="lang-bash">docker logs &lt;container-id&gt;
<span class="hljs-comment"># or</span>
docker logs mongo-express
</code></pre>
<p>You should see output similar to this:</p>
<pre><code class="lang-bash">Waiting <span class="hljs-keyword">for</span> mongo:27017...
No custom config.js found, loading config.default.js
Welcome to mongo-express 1.0.2
------------------------
Mongo Express server listening at http://0.0.0.0:8081
Server is open to allow connections from anyone (0.0.0.0)
basicAuth credentials are <span class="hljs-string">"admin:pass"</span>, it is recommended you change this <span class="hljs-keyword">in</span> your config.js!
</code></pre>
<p>This confirms that Mongo Express is up and running and ready to connect to your MongoDB instance.</p>
<p>Take note of the basicAuth credentials shown in the logs (admin:pass). If these credentials are present, you’ll need to use them when accessing Mongo Express from your browser. Later, you can change them in a custom config.js file for better security.</p>
<p>Once everything looks good in the logs, you can safely visit <a target="_blank" href="http://localhost:8081"><code>http://localhost:8081</code></a> to access the Mongo Express interface.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762957766334/f64b1f06-87a8-4ffb-b905-47e1871cca64.png" alt="mongo-express interface from http://localhost:8081 " class="image--center mx-auto" width="2880" height="1430" loading="lazy"></p>
<p>If your browser asks for a username and password when accessing Mongo Express, use the basicAuth credentials shown in the container logs:</p>
<pre><code class="lang-bash">Username: admin
Password: pass
</code></pre>
<p>These are the default credentials, and it’s <strong>strongly recommended</strong> to change them later in a custom <code>config.js</code> file for better security.</p>
<p>When you open Mongo Express, you will notice some default databases already created. For this project, we will create a new database called todos. Once it’s created, your Node.js application can connect to this database to store and retrieve data.</p>
<h2 id="heading-how-to-connect-nodejs-to-mongodb">How to Connect Node.js to MongoDB</h2>
<p>You already have MongoDB running inside a Docker container (mongo). The container exposes the default MongoDB port 27017 to the host, so any process on your laptop/desktop can reach it via <a target="_blank" href="http://localhost:27017">localhost:27017</a>.</p>
<p><strong>Important:</strong> The Node.js app is <strong>outside Docker</strong> (it’s just a regular node server.js process you start from your terminal).</p>
<p>Because the app is external, we <strong>must use</strong> <a target="_blank" href="http://localhost"><strong>localhost</strong></a> (or 127.0.0.1) as the host name – <strong>not</strong> the container name mongo.</p>
<p>Once we later containerise the Node.js app and put it on the same Docker network, we’ll switch the host to mongo. For now, keep it <a target="_blank" href="http://localhost">localhost</a>.</p>
<h3 id="heading-nodejs-backend">Node.js Backend</h3>
<p>Here’s a version of our <code>server.js</code> using MongoDB:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>);
<span class="hljs-keyword">const</span> multer = <span class="hljs-built_in">require</span>(<span class="hljs-string">"multer"</span>);
<span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">"path"</span>);
<span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>);
<span class="hljs-keyword">const</span> { MongoClient, ObjectId } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"mongodb"</span>);

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

<span class="hljs-comment">// Host = localhost  →  talks to the MongoDB container via the exposed port</span>
<span class="hljs-comment">// Port = 27017      →  default MongoDB port</span>
<span class="hljs-comment">// User / Pass       →  admin / password (the credentials you gave the container)</span>
<span class="hljs-keyword">const</span> mongoUrl = <span class="hljs-string">"mongodb://admin:password@localhost:27017"</span>;
<span class="hljs-keyword">const</span> dbName = <span class="hljs-string">"todos"</span>;
<span class="hljs-keyword">let</span> db;

MongoClient.connect(mongoUrl)
  .then(<span class="hljs-function">(<span class="hljs-params">client</span>) =&gt;</span> {
    db = client.db(dbName);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Connected to MongoDB →"</span>, dbName);
  })
  .catch(<span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"MongoDB connection error:"</span>, err));

<span class="hljs-keyword">const</span> uploadDir = path.join(__dirname, <span class="hljs-string">"uploads"</span>);
<span class="hljs-keyword">if</span> (!fs.existsSync(uploadDir)) fs.mkdirSync(uploadDir);

<span class="hljs-keyword">const</span> storage = multer.diskStorage({
  <span class="hljs-attr">destination</span>: <span class="hljs-function">(<span class="hljs-params">req, file, cb</span>) =&gt;</span> cb(<span class="hljs-literal">null</span>, uploadDir),
  <span class="hljs-attr">filename</span>: <span class="hljs-function">(<span class="hljs-params">req, file, cb</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> unique = <span class="hljs-built_in">Date</span>.now() + <span class="hljs-string">"-"</span> + <span class="hljs-built_in">Math</span>.round(<span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">1e9</span>);
    cb(<span class="hljs-literal">null</span>, <span class="hljs-string">"photo-"</span> + unique + path.extname(file.originalname));
  },
});
<span class="hljs-keyword">const</span> upload = multer({ storage });

app.use(express.static(__dirname));
app.use(<span class="hljs-string">"/uploads"</span>, express.static(uploadDir));
app.use(express.json());
app.use(express.urlencoded({ <span class="hljs-attr">extended</span>: <span class="hljs-literal">true</span> }));

app.get(<span class="hljs-string">"/todos"</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> todos = <span class="hljs-keyword">await</span> db.collection(<span class="hljs-string">"todos"</span>).find().toArray();
  res.json(todos);
});

app.post(<span class="hljs-string">"/todos"</span>, upload.single(<span class="hljs-string">"photo"</span>), <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> text = req.body.text?.trim();
  <span class="hljs-keyword">if</span> (!text) <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).json({ <span class="hljs-attr">error</span>: <span class="hljs-string">"Text required"</span> });

  <span class="hljs-keyword">const</span> todo = {
    text,
    <span class="hljs-attr">image</span>: req.file ? <span class="hljs-string">`/uploads/<span class="hljs-subst">${req.file.filename}</span>`</span> : <span class="hljs-literal">null</span>,
    <span class="hljs-attr">createdAt</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(),
  };

  <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> db.collection(<span class="hljs-string">"todos"</span>).insertOne(todo);
  todo._id = result.insertedId;
  res.json(todo);
});

<span class="hljs-comment">// Start server</span>
app.listen(PORT, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server → http://localhost:<span class="hljs-subst">${PORT}</span>`</span>);
});
</code></pre>
<h3 id="heading-frontend"><strong>Frontend</strong></h3>
<p><code>index.html</code>:</p>
<pre><code class="lang-xml"><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> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</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">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">title</span>&gt;</span>Todo + Image<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
      <span class="hljs-selector-tag">body</span> {
        <span class="hljs-attribute">font-family</span>: sans-serif;
        <span class="hljs-attribute">margin</span>: <span class="hljs-number">2rem</span>;
        <span class="hljs-attribute">max-width</span>: <span class="hljs-number">800px</span>;
      }
      <span class="hljs-selector-class">.todo</span> {
        <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ccc</span>;
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
        <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">1rem</span>;
        <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">8px</span>;
      }
      <span class="hljs-selector-class">.todo</span> <span class="hljs-selector-tag">img</span> {
        <span class="hljs-attribute">max-height</span>: <span class="hljs-number">150px</span>;
        <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">0.5rem</span>;
      }
      <span class="hljs-selector-class">.error</span> {
        <span class="hljs-attribute">color</span>: red;
      }
      <span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"text"</span>]</span> {
        <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span>;
        <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">0.5rem</span>;
      }
      <span class="hljs-selector-id">#preview</span> {
        <span class="hljs-attribute">max-width</span>: <span class="hljs-number">300px</span>;
        <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">0.5rem</span>;
        <span class="hljs-attribute">display</span>: none;
      }
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</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>Todo List with Images<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"addForm"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"textInput"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"What needs to be done?"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"file"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"imageInput"</span> <span class="hljs-attr">accept</span>=<span class="hljs-string">"image/*"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"preview"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"preview"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"addBtn"</span>&gt;</span>Add Todo<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"status"</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">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Todos<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"todos"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
      <span class="hljs-keyword">const</span> $ = <span class="hljs-built_in">document</span>.querySelector.bind(<span class="hljs-built_in">document</span>);

      <span class="hljs-keyword">const</span> textInput = $(<span class="hljs-string">"#textInput"</span>);
      <span class="hljs-keyword">const</span> imageInput = $(<span class="hljs-string">"#imageInput"</span>);
      <span class="hljs-keyword">const</span> preview = $(<span class="hljs-string">"#preview"</span>);
      <span class="hljs-keyword">const</span> addBtn = $(<span class="hljs-string">"#addBtn"</span>);
      <span class="hljs-keyword">const</span> status = $(<span class="hljs-string">"#status"</span>);
      <span class="hljs-keyword">const</span> todosDiv = $(<span class="hljs-string">"#todos"</span>);

      imageInput.addEventListener(<span class="hljs-string">"change"</span>, <span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">const</span> file = imageInput.files[<span class="hljs-number">0</span>];
        <span class="hljs-keyword">if</span> (!file) {
          preview.style.display = <span class="hljs-string">"none"</span>;
          <span class="hljs-keyword">return</span>;
        }
        <span class="hljs-keyword">const</span> reader = <span class="hljs-keyword">new</span> FileReader();
        reader.onload = <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
          preview.src = e.target.result;
          preview.style.display = <span class="hljs-string">"block"</span>;
        };
        reader.readAsDataURL(file);
      });

      addBtn.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-keyword">async</span> () =&gt; {
        <span class="hljs-keyword">const</span> text = textInput.value.trim();
        <span class="hljs-keyword">if</span> (!text) {
          status.textContent = <span class="hljs-string">"Please enter a todo text."</span>;
          status.className = <span class="hljs-string">"error"</span>;
          <span class="hljs-keyword">return</span>;
        }

        <span class="hljs-keyword">const</span> form = <span class="hljs-keyword">new</span> FormData();
        form.append(<span class="hljs-string">"text"</span>, text);
        <span class="hljs-keyword">if</span> (imageInput.files[<span class="hljs-number">0</span>]) form.append(<span class="hljs-string">"photo"</span>, imageInput.files[<span class="hljs-number">0</span>]);

        <span class="hljs-keyword">try</span> {
          <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"/todos"</span>, { <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>, <span class="hljs-attr">body</span>: form });
          <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> res.json();
          <span class="hljs-keyword">if</span> (!res.ok) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(data.error || <span class="hljs-string">"failed"</span>);
          status.textContent = <span class="hljs-string">"Todo added!"</span>;
          status.className = <span class="hljs-string">""</span>;
          textInput.value = <span class="hljs-string">""</span>;
          imageInput.value = <span class="hljs-string">""</span>;
          preview.style.display = <span class="hljs-string">"none"</span>;
          loadTodos(); <span class="hljs-comment">// refresh list</span>
        } <span class="hljs-keyword">catch</span> (err) {
          status.textContent = <span class="hljs-string">"Error: "</span> + err.message;
          status.className = <span class="hljs-string">"error"</span>;
        }
      });

      <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">loadTodos</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"/todos"</span>);
        <span class="hljs-keyword">const</span> todos = <span class="hljs-keyword">await</span> res.json();
        todosDiv.innerHTML = <span class="hljs-string">""</span>;
        todos.forEach(<span class="hljs-function">(<span class="hljs-params">t</span>) =&gt;</span> {
          <span class="hljs-keyword">const</span> div = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"div"</span>);
          div.className = <span class="hljs-string">"todo"</span>;
          div.innerHTML = <span class="hljs-string">`&lt;strong&gt;<span class="hljs-subst">${escapeHtml(t.text)}</span>&lt;/strong&gt;`</span>;
          <span class="hljs-keyword">if</span> (t.image) {
            div.innerHTML += <span class="hljs-string">`&lt;br&gt;&lt;img src="<span class="hljs-subst">${t.image}</span>" alt="todo image"&gt;`</span>;
          }
          todosDiv.appendChild(div);
        });
      }

      <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">escapeHtml</span>(<span class="hljs-params">s</span>) </span>{
        <span class="hljs-keyword">const</span> div = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"div"</span>);
        div.textContent = s;
        <span class="hljs-keyword">return</span> div.innerHTML;
      }

      loadTodos();
    </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>Now your Node.js app can connect to the MongoDB container running in Docker. Since the app is running outside Docker for now, it connects through <code>localhost:27017</code> using the credentials you set (<code>admin</code> / <code>password</code>).</p>
<p>Once connected, your Node.js backend stores and retrieves todos directly from the <code>todos</code> database in MongoDB, replacing the in-memory array. Later, if you containerize the Node.js app and put it on the same Docker network as MongoDB, you can switch the host from <code>localhost</code> to the container name <code>mongo</code>. we are getting there</p>
<p>You can get the full backend and frontend code ready to run and tweak it for your setup here: <a target="_blank" href="https://github.com/Oghenekparobo/docker_tut_js/tree/mongodb-connection">GitHub repo</a>.</p>
<h2 id="heading-how-to-use-docker-compose">How to Use Docker Compose</h2>
<p>So we now have our Node.js app connected to MongoDB and Mongo Express, both running inside containers. We’ve created the network, started the containers, and everything is talking to each other perfectly.</p>
<p>But let’s be honest: typing out all those long <code>docker run</code> commands every time can get tedious. You probably want a simpler, cleaner way to spin everything up with just one command. That’s where <strong>Docker Compose</strong> comes in.</p>
<p>Docker Compose is a tool that lets you define and run multi-container applications with a single command. Instead of manually running multiple <code>docker run</code> commands, you describe your setup in a simple <code>docker-compose.yml</code> file, specifying each service (like your Node.js app, MongoDB, and Mongo Express), their configurations, environment variables, and shared networks.</p>
<p>Basically, it lets you manage multiple containers as one project, easy to start, stop, and maintain with a single file and a single command.</p>
<p>The standard naming convention is <code>docker-compose.yml</code> (or <code>docker-compose.yaml</code>. Both work, but <code>.yml</code> is more common).</p>
<p>Docker automatically detects it when you run:</p>
<pre><code class="lang-xml">docker compose up
</code></pre>
<p>So yeah, stick with <code>docker-compose.yml</code> for convention.</p>
<p>Now, to run the containers for MongoDB and Mongo Express, we can use the following two commands, respectively:</p>
<pre><code class="lang-xml"># MongoDB container
docker run -p 27017:27017 -d \
  -e MONGO_INITDB_ROOT_USERNAME=admin \
  -e MONGO_INITDB_ROOT_PASSWORD=password \
  --name mongo \
  --network mongo-network \
  mongo

# Mongo Express container
docker run -d \
  -e ME_CONFIG_MONGODB_ADMINUSERNAME=admin \
  -e ME_CONFIG_MONGODB_ADMINPASSWORD=password \
  -e ME_CONFIG_MONGODB_SERVER=mongo \
  --name mongo-express \
  --network mongo-network \
  -p 8081:8081 \
  mongo-express
</code></pre>
<p>Now, instead of typing these long commands every time, we will combine them and run everything at once using a <strong>Docker Compose file</strong>.</p>
<p>The <code>docker-compose.yml</code> file will be located at the root of our Node.js project.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763032152636/7fc026ba-d593-4097-a34c-945b398f2aeb.png" alt="docker-composer.yml file in the root of the project" class="image--center mx-auto" width="2880" height="1612" loading="lazy"></p>
<p>Here’s how our <code>docker-compose.yml</code> file looks:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">"3.8"</span>

<span class="hljs-attr">services:</span>
  <span class="hljs-attr">mongodb:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mongo</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">mongo</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"27017:27017"</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">MONGO_INITDB_ROOT_USERNAME:</span> <span class="hljs-string">admin</span>
      <span class="hljs-attr">MONGO_INITDB_ROOT_PASSWORD:</span> <span class="hljs-string">password</span>

  <span class="hljs-attr">mongo-express:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mongo-express</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">mongo-express</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8081:8081"</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">ME_CONFIG_MONGODB_ADMINUSERNAME:</span> <span class="hljs-string">admin</span>
      <span class="hljs-attr">ME_CONFIG_MONGODB_ADMINPASSWORD:</span> <span class="hljs-string">password</span>
      <span class="hljs-attr">ME_CONFIG_MONGODB_SERVER:</span> <span class="hljs-string">mongodb</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">mongodb</span>
</code></pre>
<p>Let’s break down what’s going on here:</p>
<ul>
<li><p><code>version: "3.8"</code>: This defines the <strong>Compose file version</strong>. Each version has slightly different syntax rules and features. Version 3.8 is modern and works with the latest Docker Engine.</p>
</li>
<li><p><code>services:</code>: All the containers we want to run are defined here. In our case, two services: <code>mongodb</code> and <code>mongo-express</code>.</p>
</li>
</ul>
<p><strong>MongoDB service:</strong></p>
<ul>
<li><p><code>image: mongo</code> pulls the official MongoDB image from Docker Hub.</p>
</li>
<li><p><code>container_name: mongo</code> gives the container a friendly name.</p>
</li>
<li><p><code>ports: "27017:27017"</code> exposes MongoDB’s default port to our host, so Node.js or other apps can connect.</p>
</li>
<li><p><code>environment:</code> sets up the root username and password for MongoDB.</p>
</li>
</ul>
<p><strong>Mongo Express service:</strong></p>
<ul>
<li><p><code>image: mongo-express</code> is the official Mongo Express image.</p>
</li>
<li><p><code>container_name: mongo-express</code> is a friendly name for easier reference.</p>
</li>
<li><p><code>ports: "8081:8081"</code> exposes Mongo Express web interface on host port 8081.</p>
</li>
<li><p><code>environment:</code> let’s Mongo Express know how to connect to MongoDB (username, password, host).</p>
</li>
<li><p><code>depends_on: - mongodb</code> ensures MongoDB starts first, so Mongo Express can connect immediately.</p>
</li>
</ul>
<h3 id="heading-why-use-docker-compose">Why Use Docker Compose?</h3>
<ul>
<li><strong>Single command</strong>: Instead of running multiple long <code>docker run</code> commands, just run:</li>
</ul>
<pre><code class="lang-bash">docker compose up -d
</code></pre>
<ul>
<li><p><strong>Automatic networking</strong>: Compose creates a default network so services can communicate using their <strong>service names</strong> (<code>mongodb</code> In our case)</p>
</li>
<li><p><strong>Easier maintenance</strong>: You can stop, start, or rebuild all services with simple commands.</p>
</li>
</ul>
<p>Before we run our new <code>docker-compose.yml</code>, it’s important to make sure no conflicting containers are running. Remember, we already had MongoDB and Mongo Express running from the previous <code>docker run</code> commands.</p>
<p>To avoid conflicts (like ports already in use), we should stop and remove any running containers first.</p>
<p>Here’s how:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># List all running containers</span>
docker ps

<span class="hljs-comment"># Stop a specific container (replace &lt;container_name&gt; with mongo or mongo-express)</span>
docker stop mongo
docker stop mongo-express

<span class="hljs-comment"># Remove the stopped containers</span>
docker rm mongo
docker rm mongo-express

<span class="hljs-comment"># Optional: stop and remove all running containers at once</span>
docker stop $(docker ps -q)
docker rm $(docker ps -a -q)
</code></pre>
<ul>
<li><p><code>docker ps</code> shows currently running containers.</p>
</li>
<li><p><code>docker stop &lt;name&gt;</code> stops a container gracefully.</p>
</li>
<li><p><code>docker rm &lt;name&gt;</code> removes the container from Docker.</p>
</li>
<li><p><code>docker stop $(docker ps -q)</code> stops all running containers.</p>
</li>
<li><p><code>docker rm $(docker ps -a -q)</code> removes all containers (running or stopped).</p>
</li>
</ul>
<p>Once all previous containers are stopped and removed, we’re ready to run our Docker Compose setup safely without conflicts.</p>
<p>Now that all previous containers are stopped, we can start MongoDB and Mongo Express together using our <code>docker-compose.yml</code> file.</p>
<p>From the root of your Node.js project (where the <code>docker-compose.yml</code> file is located), run:</p>
<pre><code class="lang-bash">docker compose up -d
</code></pre>
<p>Here’s what this does:</p>
<ul>
<li><p><code>docker compose</code> tells Docker to use Compose.</p>
</li>
<li><p><code>up</code> builds (if needed) and starts all the services defined in the Compose file.</p>
</li>
<li><p><code>-d</code> runs the containers in <strong>detached mode</strong>, meaning they run in the background.</p>
</li>
</ul>
<p>After running this command, Docker will start both MongoDB and Mongo Express, connect them on the same internal network, and expose the ports we defined (<code>27017</code> for MongoDB and <code>8081</code> for Mongo Express).</p>
<p>If everything worked correctly, after running:</p>
<pre><code class="lang-bash">docker compose up -d
</code></pre>
<p>You should see output similar to this:</p>
<pre><code class="lang-bash">[+] Running 3/3
 ✔ Network docker_tut_default  Created                                                                                               0.0s 
 ✔ Container mongo             Started                                                                                               0.6s 
 ✔ Container mongo-express     Started                                                                                               0.8s 
stephenjohnson@Oghenekparobo docker_tut %
</code></pre>
<p>What this means:</p>
<ul>
<li><p><code>Network docker_tut_default Created</code>: Docker Compose automatically creates a network for your services so they can communicate with each other.</p>
</li>
<li><p><code>Container mongo Started</code>: Your MongoDB container is running.</p>
</li>
<li><p><code>Container mongo-express Started</code>: Your Mongo Express container is running.</p>
</li>
</ul>
<p>You can confirm that the containers are running by using:</p>
<pre><code class="lang-bash">docker ps
</code></pre>
<p>This will list all active containers. You should see both <code>mongo</code> and <code>mongo-express</code> with their respective ports (<code>27017</code> for MongoDB and <code>8081</code> for Mongo Express) exposed.</p>
<ul>
<li><p>To access Mongo Express, open your browser and go to <a target="_blank" href="http://localhost:8081">http://localhost:8081</a> to interact with MongoDB through the web interface.</p>
</li>
<li><p>To access MongoDB, your Node.js app can connect to MongoDB at <code>localhost:27017</code> using the credentials you set in the Compose file.</p>
</li>
</ul>
<p>Compared to running long <code>docker run</code> commands for each container, using Docker Compose is easier because:</p>
<ul>
<li><p>Starts multiple containers with one command.</p>
</li>
<li><p>Automatically sets up networking between containers.</p>
</li>
<li><p>Makes it easier to stop, remove, or rebuild containers later.</p>
</li>
</ul>
<p>In short, Docker Compose simplifies and organizes everything, making it much easier to manage your development environment.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763034021801/c4f806d2-080f-4b52-9f36-7b2044a7f8c5.png" alt="docker compose up -d succesfuly created the containers and docker ps shows the containers" class="image--center mx-auto" width="1962" height="746" loading="lazy"></p>
<p>At this stage, it’s important to know that any data you add to MongoDB is temporary. If you stop or remove your containers and then start them again, you will notice that all your data is gone. This happens because data inside a container isn’t persistent by default.</p>
<p>Don’t worry, this is expected, and we’ll cover how to make data persistent later in the tutorial when we introduce <strong>Docker volumes.</strong> For now, just be aware that each time you restart your containers, MongoDB starts fresh with no previous data.</p>
<p>You can get a full sample, including the Dockerfile <strong>and</strong> the docker‑compose file, <a target="_blank" href="https://github.com/Oghenekparobo/docker_tut_js/tree/docker-compose">here</a>.</p>
<h2 id="heading-how-to-build-our-own-docker-image">How to Build Our Own Docker Image</h2>
<p>Now that we have tested our Node.js application locally and seen it working perfectly with MongoDB and Mongo Express, the next step is preparing it for deployment.</p>
<p>Running the app directly on our machine works fine for development, but it’s not practical when we want to move it to another environment or server. By creating a Docker image, we can package the application together with all its dependencies, configuration, and environment setup into a single, portable unit. This image can then run anywhere Docker is installed, ensuring our app works the same way across development, testing, and production.</p>
<p>In short, building a Docker image is how we containerize our app and make it deployment-ready.</p>
<p>In order to containerize our Todo app, we need a <strong>Dockerfile</strong>. A Dockerfile is essentially a blueprint that tells Docker how to build an image for our application. It defines the base environment, copies our application code, installs dependencies, and specifies how the app should start. With this blueprint, Docker can create a consistent image that behaves the same way on any machine, making our Node.js app fully portable and ready for deployment.</p>
<p>In our Dockerfile, notice the capital <code>D</code>, which is the standard naming convention. Place this file in the <strong>root directory</strong> of your Node.js project. In simple projects like ours, our main app file (like <code>server.js</code> or <code>index.js</code>) is usually in the root too, along with <code>package.json</code>. Docker will use this file as a blueprint to build a container image of your application.</p>
<p>If your main app file is inside a subfolder, that’s fine too. Just make sure the Dockerfile’s <code>COPY</code> and <code>CMD</code> commands point to the correct location. The important thing is that the Dockerfile lives in the root so Docker knows where to start building your app.</p>
<p>Here’s how the contents of our Dockerfile look:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Use full Node 18 (Debian-based)</span>
<span class="hljs-string">FROM</span> <span class="hljs-string">node:18</span>

<span class="hljs-comment"># Set environment variables</span>
<span class="hljs-string">ENV</span> <span class="hljs-string">MONGO_DB_USERNAME=admin</span> <span class="hljs-string">\</span>
    <span class="hljs-string">MONGO_DB_PASSWORD=password</span>

<span class="hljs-comment"># Set working directory</span>
<span class="hljs-string">WORKDIR</span> <span class="hljs-string">/home/app</span>

<span class="hljs-comment"># Copy package files</span>
<span class="hljs-string">COPY</span> <span class="hljs-string">package*.json</span> <span class="hljs-string">./</span>

<span class="hljs-comment"># Install dependencies</span>
<span class="hljs-string">RUN</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span>

<span class="hljs-comment"># Copy source code</span>
<span class="hljs-string">COPY</span> <span class="hljs-string">.</span> <span class="hljs-string">.</span>

<span class="hljs-comment"># Expose port</span>
<span class="hljs-string">EXPOSE</span> <span class="hljs-number">3000</span>

<span class="hljs-comment"># Start the app</span>
<span class="hljs-string">CMD</span> [<span class="hljs-string">"node"</span>, <span class="hljs-string">"server.js"</span>]
</code></pre>
<p>Let’s see what’s going on here:</p>
<ul>
<li><p><code>FROM node:13-alpine</code> is the base image for our container. It comes with Node.js installed and is very lightweight, keeping the image small.</p>
</li>
<li><p><code>ENV MONGO_DB_USERNAME=admin \ MONGO_DB_PASSWORD=password</code> sets environment variables inside the container so the Node.js app can connect to MongoDB.</p>
</li>
<li><p><code>WORKDIR /home/app</code> sets the working directory inside the container. All subsequent commands like <code>COPY</code> or <code>RUN</code> will run relative to this folder.</p>
</li>
<li><p><code>COPY . .</code> copies all files from your local project into the container’s working directory. This includes your <code>server.js</code>, <code>package.json</code>, and any other files needed to run the app.</p>
</li>
<li><p><code>RUN npm install</code> installs all the Node.js dependencies listed in <code>package.json</code> inside the container.</p>
</li>
<li><p><code>EXPOSE 3000</code> tells Docker that the container will listen on port 3000, which is the port our Node.js app runs on.</p>
</li>
<li><p><code>CMD ["node", "server.js"]</code> defines the command that runs when the container starts, which launches our Node.js server.</p>
</li>
</ul>
<p>By placing this Dockerfile in the root of your project, Docker knows exactly where to find your app’s files and dependencies. When we build the image, it packages everything inside a portable container that can run anywhere Docker is installed, making deployment straightforward and consistent.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763037649231/a831ccc0-5b84-4e70-82bf-08ef7de85ddf.png" alt="Dockerfile VS CODE Illustration" class="image--center mx-auto" width="2880" height="1382" loading="lazy"></p>
<p>Now that we have our Dockerfile ready, the next step is to build the Docker image for our Node.js app.</p>
<p>To build the image, open your terminal, make sure you are in the root directory of your project (where the Dockerfile is), and run:</p>
<pre><code class="lang-bash">docker build -t todo-app:1.0 .
</code></pre>
<ul>
<li><p><code>todo-app</code> is the name of your image.</p>
</li>
<li><p><code>:1.0</code> is the version tag (you can use any versioning scheme, like <code>1.0</code>, <code>v1</code>, <code>latest</code>, etc.).</p>
</li>
<li><p><code>.</code> tells Docker to use the current folder (root of your project) as the build context.</p>
</li>
</ul>
<p>After running:</p>
<pre><code class="lang-bash">docker build -t todo-app:1.0 .
</code></pre>
<p>Docker reads your Dockerfile, packages your Node.js app with all its dependencies, and creates a Docker image. You can confirm the image exists by running:</p>
<pre><code class="lang-bash">docker images
</code></pre>
<p>You should see output like this:</p>
<pre><code class="lang-bash">REPOSITORY      TAG       IMAGE ID       CREATED          SIZE
todo-app        1.0       d85dd4ed97f9   45 seconds ago   147MB
mongo           latest    1d659cebf5e9   2 weeks ago      894MB
mongo-express   latest    1133e12468c7   20 months ago    182MB
</code></pre>
<p>This shows that your <code>todo-app</code> image has been created successfully, alongside the images for MongoDB and Mongo Express.</p>
<h3 id="heading-running-your-nodejs-app-container">Running Your Node.js App Container</h3>
<p>Now that the image exists, the next step is to run a container from it. A container is basically a running instance of your image. To do this:</p>
<pre><code class="lang-bash">docker run todo-app:1.0
</code></pre>
<p>Here’s what this command does:</p>
<ul>
<li><p><code>docker run</code> starts a new container from the image.</p>
</li>
<li><p><code>todo-app:1.0</code> tells Docker which image to use (the one we just built).</p>
</li>
</ul>
<p>Once this runs, your Node.js app will be live inside a container, separate from your local environment. You can open your browser at <a target="_blank" href="http://localhost:3000"><code>http://localhost:3000</code></a> and see your Todo app working just like it did locally.</p>
<p>To see all running containers, use:</p>
<pre><code class="lang-bash">docker ps
</code></pre>
<p>You’ll see something like:</p>
<pre><code class="lang-bash">CONTAINER ID   IMAGE           COMMAND         CREATED       STATUS       PORTS                  NAMES
d85dd4ed97f9   todo-app:1.0    <span class="hljs-string">"node server.js"</span>  10s ago      Up 10s       0.0.0.0:3000-&gt;3000/tcp   awesome_todo
</code></pre>
<p>This confirms your container is running. If you ever need to stop it:</p>
<pre><code class="lang-bash">docker stop &lt;container-id&gt;
</code></pre>
<h3 id="heading-troubleshooting-errors">Troubleshooting Errors</h3>
<p>We started facing some issues here: when you run <code>docker run todo-app:1.0</code> You'll see an error like this:</p>
<pre><code class="lang-yaml"><span class="hljs-string">Server</span> <span class="hljs-string">→</span> <span class="hljs-string">http://localhost:3000</span> 
<span class="hljs-attr">MongoDB connection error: MongoServerSelectionError:</span> <span class="hljs-string">getaddrinfo</span> <span class="hljs-string">ENOTFOUND</span> <span class="hljs-string">mongodb</span>
    <span class="hljs-string">at</span> <span class="hljs-string">Topology.selectServer</span> <span class="hljs-string">(/home/app/node_modules/mongodb/lib/sdam/topology.js:346:38)</span>
    <span class="hljs-string">...</span>
    [<span class="hljs-string">cause</span>
</code></pre>
<p>especially when you try to perform an operation like creating a todo list.</p>
<p>The error <code>getaddrinfo ENOTFOUND mongodb</code> tells us that your Node.js container can't find MongoDB. Even though MongoDB is running in another container, your app container is isolated and doesn't know how to reach it.</p>
<h4 id="heading-why-this-happens">Why This Happens:</h4>
<p>Remember in our <code>server.js</code>, we connect to MongoDB using:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> mongoUrl = <span class="hljs-string">"mongodb://admin:password@localhost:27017"</span>;
</code></pre>
<p>The problem is with <code>localhost</code>. When you run your app locally on your machine (not in Docker), <code>localhost</code> works perfectly because MongoDB is running on the same machine. But when your app runs inside a Docker container, <code>localhost</code> refers to the container itself, not your host machine or other containers.</p>
<p>Think of it like this:</p>
<ul>
<li><p><strong>Running locally:</strong> Your app and MongoDB are like two people in the same room, <code>localhost</code> works</p>
</li>
<li><p><strong>Running in Docker:</strong> Each container is like a separate room, <code>localhost</code> only refers to that specific room</p>
</li>
</ul>
<h3 id="heading-the-solution"><strong>The Solution</strong></h3>
<p>We need to change the MongoDB connection URL to use the Docker service name instead of <code>localhost</code>. Update your <code>server.js</code> file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> mongoUrl = <span class="hljs-string">"mongodb://admin:password@localhost:27017"</span>;
</code></pre>
<p>To this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> mongoUrl = <span class="hljs-string">"mongodb://admin:password@mongodb:27017"</span>;
</code></pre>
<p>Here's the complete updated <code>server.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>);
<span class="hljs-keyword">const</span> multer = <span class="hljs-built_in">require</span>(<span class="hljs-string">"multer"</span>);
<span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">"path"</span>);
<span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>);
<span class="hljs-keyword">const</span> { MongoClient, ObjectId } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"mongodb"</span>);

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

<span class="hljs-comment">// Host = localhost  →  talks to the MongoDB container via the exposed port</span>
<span class="hljs-comment">// Port = 27017      →  default MongoDB port</span>
<span class="hljs-comment">// User / Pass       →  admin / password (the credentials you gave the container)</span>
<span class="hljs-keyword">const</span> mongoUrl = <span class="hljs-string">"mongodb://admin:password@mongodb:27017"</span>;
<span class="hljs-keyword">const</span> dbName = <span class="hljs-string">"todos"</span>;
<span class="hljs-keyword">let</span> db;

MongoClient.connect(mongoUrl)
  .then(<span class="hljs-function">(<span class="hljs-params">client</span>) =&gt;</span> {
    db = client.db(dbName);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Connected to MongoDB →"</span>, dbName);
  })
  .catch(<span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"MongoDB connection error:"</span>, err));

<span class="hljs-keyword">const</span> uploadDir = path.join(__dirname, <span class="hljs-string">"uploads"</span>);
<span class="hljs-keyword">if</span> (!fs.existsSync(uploadDir)) fs.mkdirSync(uploadDir);

<span class="hljs-keyword">const</span> storage = multer.diskStorage({
  <span class="hljs-attr">destination</span>: <span class="hljs-function">(<span class="hljs-params">req, file, cb</span>) =&gt;</span> cb(<span class="hljs-literal">null</span>, uploadDir),
  <span class="hljs-attr">filename</span>: <span class="hljs-function">(<span class="hljs-params">req, file, cb</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> unique = <span class="hljs-built_in">Date</span>.now() + <span class="hljs-string">"-"</span> + <span class="hljs-built_in">Math</span>.round(<span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">1e9</span>);
    cb(<span class="hljs-literal">null</span>, <span class="hljs-string">"photo-"</span> + unique + path.extname(file.originalname));
  },
});
<span class="hljs-keyword">const</span> upload = multer({ storage });

app.use(express.static(__dirname));
app.use(<span class="hljs-string">"/uploads"</span>, express.static(uploadDir));
app.use(express.json());
app.use(express.urlencoded({ <span class="hljs-attr">extended</span>: <span class="hljs-literal">true</span> }));

app.get(<span class="hljs-string">"/todos"</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> todos = <span class="hljs-keyword">await</span> db.collection(<span class="hljs-string">"todos"</span>).find().toArray();
  res.json(todos);
});

app.post(<span class="hljs-string">"/todos"</span>, upload.single(<span class="hljs-string">"photo"</span>), <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> text = req.body.text?.trim();
  <span class="hljs-keyword">if</span> (!text) <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).json({ <span class="hljs-attr">error</span>: <span class="hljs-string">"Text required"</span> });

  <span class="hljs-keyword">const</span> todo = {
    text,
    <span class="hljs-attr">image</span>: req.file ? <span class="hljs-string">`/uploads/<span class="hljs-subst">${req.file.filename}</span>`</span> : <span class="hljs-literal">null</span>,
    <span class="hljs-attr">createdAt</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(),
  };

  <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> db.collection(<span class="hljs-string">"todos"</span>).insertOne(todo);
  todo._id = result.insertedId;
  res.json(todo);
});

<span class="hljs-comment">// Start server</span>
app.listen(PORT, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server → http://localhost:<span class="hljs-subst">${PORT}</span>`</span>);
});
</code></pre>
<h3 id="heading-why-mongodb-works">Why <code>mongodb</code> Works</h3>
<p>The hostname <code>mongodb</code> matches the service name we defined in our <code>docker-compose.yml</code>:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">services:</span>
  <span class="hljs-attr">mongodb:</span>    <span class="hljs-comment"># ← This is the hostname other containers use</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mongo</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">mongo</span>
    <span class="hljs-string">...</span>
</code></pre>
<p>When containers run in the same Docker Compose network, Docker provides an internal DNS that resolves service names to the correct container IP addresses. So when your app tries to connect to <code>mongodb:27017</code>, Docker automatically routes it to the MongoDB container.</p>
<h3 id="heading-rebuild-your-docker-image">Rebuild Your Docker Image</h3>
<p>Now that we have updated the code, we need to rebuild the Docker image to include this change:</p>
<pre><code class="lang-bash">docker build -t todo-app:1.0 .
``

You should see output confirming the build completed successfully:
```
[+] Building 8.1s (10/10) FINISHED
 =&gt; [internal] load build definition from Dockerfile
 =&gt; =&gt; transferring dockerfile: 443B
 ...
 =&gt; =&gt; naming to docker.io/library/todo-app:1.0
</code></pre>
<h3 id="heading-add-your-app-to-docker-compose">Add Your App to Docker Compose</h3>
<p>Now update your <code>docker-compose.yml</code> file to include the <code>todo-app</code> service:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">"3.8"</span>

<span class="hljs-attr">services:</span>
  <span class="hljs-attr">mongodb:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mongo</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">mongo</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"27017:27017"</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">MONGO_INITDB_ROOT_USERNAME:</span> <span class="hljs-string">admin</span>
      <span class="hljs-attr">MONGO_INITDB_ROOT_PASSWORD:</span> <span class="hljs-string">password</span>

  <span class="hljs-attr">mongo-express:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mongo-express</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">mongo-express</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8081:8081"</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">ME_CONFIG_MONGODB_ADMINUSERNAME:</span> <span class="hljs-string">admin</span>
      <span class="hljs-attr">ME_CONFIG_MONGODB_ADMINPASSWORD:</span> <span class="hljs-string">password</span>
      <span class="hljs-attr">ME_CONFIG_MONGODB_SERVER:</span> <span class="hljs-string">mongodb</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">mongodb</span>

  <span class="hljs-attr">todo-app:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">todo-app:1.0</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">todo-app</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"3000:3000"</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">mongodb</span>
</code></pre>
<p>The <code>todo-app</code> service includes:</p>
<ul>
<li><p><strong>image: todo-app:1.0</strong> that uses the Docker image we just rebuilt</p>
</li>
<li><p><strong>container_name: todo-app</strong> that gives the container a friendly name</p>
</li>
<li><p><strong>ports: "3000:3000"</strong> that exposes the app on port 3000</p>
</li>
<li><p><strong>depends_on: mongodb</strong> that ensures MongoDB starts before the app</p>
</li>
</ul>
<h3 id="heading-start-all-services">Start All Services</h3>
<p>First, stop any running containers:</p>
<pre><code class="lang-bash">docker compose down
</code></pre>
<p><strong>If you have port 3000 running in your local system, then stop it (that is, free up port 3000).</strong></p>
<p>We were running the server locally before, but now that we’ve built a Docker image, the app runs inside a container, so it’s no longer dependent on the local machine’s environment.</p>
<pre><code class="lang-yaml"><span class="hljs-string">node</span> <span class="hljs-string">server.js</span>
<span class="hljs-string">Server</span> <span class="hljs-string">→</span> <span class="hljs-string">http://localhost:3000</span>
</code></pre>
<p>Now stop it with Ctrl + C in that terminal. That’s it.</p>
<p>Then start everything together:</p>
<pre><code class="lang-bash">docker compose up -d
```

You should see:
```
[+] Running 4/4
 ✔ Network docker_tut_default  Created
 ✔ Container mongo             Started
 ✔ Container mongo-express     Started
 ✔ Container todo-app          Started
</code></pre>
<h3 id="heading-verify-everything-works">Verify Everything Works</h3>
<p>Check that all containers are running:</p>
<pre><code class="lang-bash">docker ps
```

Expected output:
```
CONTAINER ID   IMAGE           COMMAND                  CREATED          STATUS          PORTS                      NAMES
a1b2c3d4e5f6   todo-app:1.0    <span class="hljs-string">"node server.js"</span>         30 seconds ago   Up 28 seconds   0.0.0.0:3000-&gt;3000/tcp     todo-app
3d7c797fde1d   mongo-express   <span class="hljs-string">"/sbin/tini -- /dock…"</span>   30 seconds ago   Up 29 seconds   0.0.0.0:8081-&gt;8081/tcp     mongo-express
4511ade73c38   mongo           <span class="hljs-string">"docker-entrypoint.s…"</span>   30 seconds ago   Up 29 seconds   0.0.0.0:27017-&gt;27017/tcp   mongo
```

<span class="hljs-comment">## Test Your Application</span>

Now <span class="hljs-built_in">let</span><span class="hljs-string">'s verify everything works:

### 1. Access Your Todo App
Open your browser and go to:
```
http://localhost:3000
```

### 2. Create Some Todos
Add a few todo items to test the functionality. Try uploading images too!

### 3. Verify in Mongo Express
Open Mongo Express:
```
http://localhost:8081</span>
</code></pre>
<p>Navigate to the <code>todos</code> database, then the <code>todos</code> collection. You should see all the todos you just created with their complete data.</p>
<h3 id="heading-what-changed-and-why-it-works">What Changed and Why It Works</h3>
<p><strong>Before the fix:</strong></p>
<ul>
<li><p>Connection string used <code>localhost:27017</code> ❌</p>
</li>
<li><p>Container looked for MongoDB on itself</p>
</li>
<li><p>Connection failed with <code>ENOTFOUND</code> error</p>
</li>
</ul>
<p><strong>After the fix:</strong></p>
<ul>
<li><p>Connection string uses <code>mongodb:27017</code> ✅</p>
</li>
<li><p>Docker's internal DNS resolves <code>mongodb</code> to the MongoDB container</p>
</li>
<li><p>Connection succeeds and data flows properly</p>
</li>
</ul>
<p>This is a crucial lesson in Docker networking: containers communicate using service names, not <code>localhost</code>. Docker Compose automatically creates a network where all services can find each other by name.</p>
<h3 id="heading-how-to-manage-your-containers">How to Manage Your Containers</h3>
<p>Here’s a quick overview of how to manage your containers once you have them up and running. You’ll typically use these common commands:</p>
<p><strong>Stop all services:</strong></p>
<pre><code class="lang-bash">docker compose down
</code></pre>
<p><strong>View logs from your app:</strong></p>
<pre><code class="lang-bash">docker compose logs todo-app
</code></pre>
<p><strong>View logs in real-time:</strong></p>
<pre><code class="lang-bash">docker compose logs -f todo-app
</code></pre>
<p><strong>Rebuild after code changes:</strong></p>
<pre><code class="lang-bash">docker build -t todo-app:1.0 .
docker compose up -d --force-recreate todo-app
</code></pre>
<p>Your application is now fully containerized and production-ready. All three services work together seamlessly, and you can deploy this entire stack anywhere Docker is supported with just the <code>docker-compose.yml</code> file and your built image.</p>
<p>Get the full updated code <a target="_blank" href="https://github.com/Oghenekparobo/docker_tut_js/tree/docker-image">here</a>.</p>
<h2 id="heading-how-to-create-a-private-docker-repository">How to Create a Private Docker Repository</h2>
<p>Now we want to store our custom Docker image in a private container registry (instead of our local machine only). This gives you three major advantages:</p>
<ol>
<li><p><strong>Controlled access</strong> – Only people or servers you explicitly authorize can pull (or push) the image. Your code and dependencies stay private and secure.</p>
</li>
<li><p><strong>Reliable distribution</strong> – Anyone (or any server) with the correct AWS credentials can pull the exact same image from anywhere in the world, eliminating “it works on my machine” problems.</p>
</li>
<li><p><strong>Versioning and lifecycle management</strong> – You can keep multiple tagged versions (1.0, 2.0, latest, and so on) and easily roll back if needed.</p>
</li>
</ol>
<p>The first step is to create a private Docker repository, also known as a container registry. In this case, we will use <a target="_blank" href="https://aws.amazon.com/ecr/"><strong>AWS Elastic Container Registry (ECR)</strong></a>. Amazon ECR is a fully managed container registry that makes it easy to store, manage, share, and deploy your container images and artifacts securely from anywhere.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763044840247/bbfb5cfa-aa22-4a4f-a5e4-da91cd539063.png" alt="Amazon ECR Landing page" class="image--center mx-auto" width="2880" height="1696" loading="lazy"></p>
<p>Once you’re on the home page, just click on the <strong>Create</strong> button. Name the repository the same as your image, todo-app, and then click Create to finalize the setup.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763045060132/b2f8b287-026c-47f6-bd44-9fbad62da0f5.png" alt="creating our repository on AWS ECR" class="image--center mx-auto" width="2880" height="1696" loading="lazy"></p>
<p>Don’t worry about the extra options – this isn’t an AWS tutorial.</p>
<p><strong>Note:</strong> In AWS ECR, each image has its own repository, where we store the different tagged versions of that image.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763045250855/477e4566-8f47-41f8-93e6-4c3bcb28668f.png" alt="AWS ECR our todo-app empty repository" class="image--center mx-auto" width="2880" height="1524" loading="lazy"></p>
<p>Now, to push our image into the private repository, we need to do two things. First, we have to log in to the private repo. This is necessary because you’ll need authenticate yourself before AWS allows you to push anything. In other words, when you push your local image to the repo, you’re basically saying, <em>“Yes, I have access to this registry. Here are my credentials.”</em></p>
<p>In our case, since we’re using AWS ECR, we will authenticate through AWS instead of typing our username and password manually.</p>
<h3 id="heading-step-1-get-your-aws-access-keys">Step 1: Get Your AWS Access Keys</h3>
<p>To locate your access keys in the AWS console, follow these steps:</p>
<ol>
<li><p>Log in to the AWS Console at <a target="_blank" href="https://console.aws.amazon.com">https://console.aws.amazon.com</a></p>
</li>
<li><p>Click your account name (top right corner) and go to Security Credentials</p>
</li>
<li><p>Scroll down to "Access keys" section</p>
</li>
<li><p>If you don't have an access key:</p>
<ul>
<li><p>Click "Create access key"</p>
</li>
<li><p>Select "Command Line Interface (CLI)"</p>
</li>
<li><p>Check the confirmation box and click Next</p>
</li>
<li><p>Add a description (optional) and click "Create access key"</p>
</li>
</ul>
</li>
<li><p><strong>IMPORTANT</strong>: Copy both the <strong>access key ID</strong> (looks like: <code>AKIAIOSFODNN7EXAMPLE</code>) and the <strong>secret access key</strong> (looks like: <code>wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY</code>). <strong>Save these immediately.</strong> The secret key is only shown once. If you lose it, you'll need to create a new key pair.</p>
</li>
</ol>
<p>Alternatively, if someone else manages your AWS account, you’ll need to ask your AWS administrator for:</p>
<ul>
<li><p>An IAM user with ECR permissions</p>
</li>
<li><p>The Access Key ID and Secret Access Key for that user</p>
</li>
</ul>
<h3 id="heading-step-2-check-if-aws-cli-is-installed">Step 2: Check if AWS CLI is installed</h3>
<p>You can do this by running this:</p>
<pre><code class="lang-bash">aws --version
</code></pre>
<h3 id="heading-step-3-configure-aws-cli-with-your-credentials">Step 3: Configure AWS CLI with your credentials</h3>
<p>Here’s how you can do this:</p>
<pre><code class="lang-bash">aws configure
```

It will prompt you <span class="hljs-keyword">for</span> 4 things:
```
AWS Access Key ID [None]: &lt;paste your Access Key ID here&gt;
AWS Secret Access Key [None]: &lt;paste your Secret Access Key here&gt;
Default region name [None]: eu-north-1 or any region of your choice
Default output format [None]: json
</code></pre>
<p>Just paste your keys when prompted, type <code>eu-north-1</code> or any region of your choice for region, and <code>json</code> for format (or just press Enter for format).</p>
<h3 id="heading-step-4-test-your-aws-configuration">Step 4: Test your AWS configuration</h3>
<p>Now you’ll want to test your config to make sure everything is set up properly:</p>
<pre><code class="lang-bash">aws sts get-caller-identity
</code></pre>
<p>This should show your AWS account details if everything is configured correctly.</p>
<h3 id="heading-step-5-login-to-ecr-docker-registry">Step 5: Login to ECR (Docker Registry)</h3>
<p>Now, login to ECR:</p>
<pre><code class="lang-bash">aws ecr get-login-password --region eu-north-1 | docker login --username AWS --password-stdin 244836489456.dkr.ecr.eu-north-1.amazonaws.com
</code></pre>
<p>You should see: <strong>"Login Succeeded"</strong>.</p>
<h3 id="heading-understanding-image-naming-in-docker-repositories">Understanding Image Naming in Docker Repositories</h3>
<p>Every Docker image has a name that tells Docker where to find or store it. For example, when you run:</p>
<pre><code class="lang-yaml"><span class="hljs-string">docker</span> <span class="hljs-string">pull</span> <span class="hljs-string">mongo:4.2</span>
</code></pre>
<p>Docker is actually pulling from:</p>
<pre><code class="lang-yaml"><span class="hljs-string">docker.io/library/mongo:4.2</span>
</code></pre>
<p>Here’s what’s happening:</p>
<ul>
<li><p><a target="_blank" href="http://docker.io"><code>docker.io</code></a> is the registry (in this case, Docker Hub)</p>
</li>
<li><p><code>library</code> is the default namespace for official images</p>
</li>
<li><p><code>mongo</code> is the repository name</p>
</li>
<li><p><code>4.2</code> is the image tag</p>
</li>
</ul>
<p>If you build a local image like <code>todo-app:1.0</code>, that image exists only on your machine. Docker won’t know where to push it unless you include the full registry path.</p>
<p>For AWS ECR, the image name must include your ECR registry URL. For example:</p>
<pre><code class="lang-yaml"><span class="hljs-string">docker</span> <span class="hljs-string">tag</span> <span class="hljs-string">todo-app:1.0</span> <span class="hljs-number">244836489456.</span><span class="hljs-string">dkr.ecr.eu-north-1.amazonaws.com/todo-app:1.0</span>
</code></pre>
<p>Then you can push it with:</p>
<pre><code class="lang-yaml"><span class="hljs-string">docker</span> <span class="hljs-string">push</span> <span class="hljs-number">244836489456.</span><span class="hljs-string">dkr.ecr.eu-north-1.amazonaws.com/todo-app:1.0</span>
</code></pre>
<p>Without that full path, Docker won’t know <em>which</em> remote repository you’re referring to. That’s why just <code>todo-app:1.0</code> alone won’t work.</p>
<h3 id="heading-step-6-build-tag-and-push-your-image">Step 6: Build, Tag, and Push your image</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763050402411/f4afd0ed-bc2a-4bf2-9bc4-4d373a54bab4.png" alt="aws push commands for the ecr todo-app repo " class="image--center mx-auto" width="2880" height="1524" loading="lazy"></p>
<pre><code class="lang-bash"><span class="hljs-comment"># Tag your local image with the full ECR path</span>
docker tag todo-app:1.0 244836489456.dkr.ecr.eu-north-1.amazonaws.com/todo-app:1.0

<span class="hljs-comment"># Now push it</span>
docker push 244836489456.dkr.ecr.eu-north-1.amazonaws.com/todo-app:1.0
</code></pre>
<p>⚠️ <strong>Note:</strong> Be careful when tagging and pushing your image, as every ECR repository URL is tied to a specific AWS account and region.</p>
<p>For example, in this tutorial, we’re using:</p>
<pre><code class="lang-yaml"><span class="hljs-number">244836489456.</span><span class="hljs-string">dkr.ecr.eu-north-1.amazonaws.com</span>
</code></pre>
<p>But your own ECR URL will be different depending on your AWS account and the region you selected (like <code>us-east-1</code>, <code>ap-south-1</code>, and so on).</p>
<p>So before you run your <code>docker tag</code> or <code>docker push</code> commands, make sure to replace the registry URL and region with your own.</p>
<p>If you don’t, Docker will throw errors like <em>“tag does not exist”</em> or <em>“repository not found.”</em></p>
<p>In short, stay calm, double-check your region, and always confirm the exact ECR URL shown in your AWS console before pushing.</p>
<p>If you successfully ran Step 6, you should see output similar to this in your terminal:</p>
<pre><code class="lang-yaml"><span class="hljs-string">stephenjohnson@Oghenekparobo</span> <span class="hljs-string">docker_tut</span> <span class="hljs-string">%</span> <span class="hljs-string">docker</span> <span class="hljs-string">tag</span> <span class="hljs-string">todo-app:1.0</span> <span class="hljs-number">244836489456.</span><span class="hljs-string">dkr.ecr.eu-north-1.amazonaws.com/todo-app:1.0</span>
<span class="hljs-string">stephenjohnson@Oghenekparobo</span> <span class="hljs-string">docker_tut</span> <span class="hljs-string">%</span> <span class="hljs-string">docker</span> <span class="hljs-string">push</span> <span class="hljs-number">244836489456.</span><span class="hljs-string">dkr.ecr.eu-north-1.amazonaws.com/todo-app:1.0</span>
<span class="hljs-string">The</span> <span class="hljs-string">push</span> <span class="hljs-string">refers</span> <span class="hljs-string">to</span> <span class="hljs-string">repository</span> [<span class="hljs-number">244836489456.</span><span class="hljs-string">dkr.ecr.eu-north-1.amazonaws.com/todo-app</span>]
<span class="hljs-attr">4f94b5cbe8ab:</span> <span class="hljs-string">Pushed</span> 
<span class="hljs-attr">85ba7bf54231:</span> <span class="hljs-string">Pushed</span> 
<span class="hljs-attr">4ea46a43fa07:</span> <span class="hljs-string">Pushed</span> 
<span class="hljs-attr">dee30873f229:</span> <span class="hljs-string">Pushed</span> 
<span class="hljs-attr">e78159dbd370:</span> <span class="hljs-string">Pushed</span> 
<span class="hljs-attr">a358a725b813:</span> <span class="hljs-string">Pushed</span> 
<span class="hljs-attr">cd8a6003174c:</span> <span class="hljs-string">Pushed</span> 
<span class="hljs-attr">abb63e49e652:</span> <span class="hljs-string">Pushed</span> 
<span class="hljs-attr">6cc65bdde70e:</span> <span class="hljs-string">Pushed</span> 
<span class="hljs-attr">41a4e3939504:</span> <span class="hljs-string">Pushed</span> 
<span class="hljs-attr">3520c50ae60e:</span> <span class="hljs-string">Pushed</span> 
<span class="hljs-attr">75ba6634710f:</span> <span class="hljs-string">Pushed</span> 
<span class="hljs-attr">1.0: digest: sha256:51f07267936fc94d9b677db8a760801e6c5fd4764f4bb2bd7b4dd150c756a39b size:</span> <span class="hljs-number">2842</span>
</code></pre>
<p>This confirms your image was successfully pushed to your private AWS ECR repository.</p>
<p>You can now go to the AWS Management Console and then ECR, and you should see your <code>todo-app</code> image listed there, along with the tag <code>1.0</code>.</p>
<p>At this point, your image is safely stored in AWS ECR and ready to be pulled or deployed anywhere that has access to your repository.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763066904897/fe44a2e2-1e20-4ff5-88ec-799475b2fe0d.png" alt="your image now deployed on AWS ECR" class="image--center mx-auto" width="2880" height="1448" loading="lazy"></p>
<h2 id="heading-assignment-create-and-push-a-new-version-of-your-app"><strong>Assignment: Create and Push a New Version of Your App</strong></h2>
<p>Now that your first image (<code>todo-app:1.0</code>) has been successfully pushed to AWS ECR, it’s time to simulate a real-world workflow where developers make updates and release new versions of their applications.</p>
<p>Now, you’ll make a small change to your Node.js app, rebuild it, and push the updated version as <code>todo-app:2.0</code>.</p>
<h3 id="heading-deploying-our-image">Deploying Our Image</h3>
<p>Now it’s time to deploy our image using Docker Compose.</p>
<p>Up to this point, we have been running our app using a local image:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">image:</span> <span class="hljs-string">todo-app:1.0</span>
</code></pre>
<p>But now that your image lives inside AWS ECR, we need to replace that line with the full ECR image URI, because Docker must know exactly where to pull the image from.</p>
<p>Local image:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">image:</span> <span class="hljs-string">todo-app:1.0</span>
</code></pre>
<p>Private repository image (ECR):</p>
<pre><code class="lang-yaml"><span class="hljs-attr">image:</span> <span class="hljs-number">244836489456.</span><span class="hljs-string">dkr.ecr.eu-north-1.amazonaws.com/todo-app:1.0</span>
</code></pre>
<p>Docker cannot magically guess where “todo-app:1.0” is stored. If you don’t include the full registry URL, Docker will assume it’s looking at your <strong>local machine</strong>, not AWS.</p>
<p>Here is the clean, fixed, properly formatted docker-compose file that pulls your app from ECR:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">"3.8"</span>

<span class="hljs-attr">services:</span>
  <span class="hljs-attr">my-app:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-number">244836489456.</span><span class="hljs-string">dkr.ecr.eu-north-1.amazonaws.com/todo-app:1.0</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">my-app</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"3000:3000"</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">mongodb</span>

  <span class="hljs-attr">mongodb:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mongo</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">mongo</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"27017:27017"</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">MONGO_INITDB_ROOT_USERNAME:</span> <span class="hljs-string">admin</span>
      <span class="hljs-attr">MONGO_INITDB_ROOT_PASSWORD:</span> <span class="hljs-string">password</span>

  <span class="hljs-attr">mongo-express:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mongo-express</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">mongo-express</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8081:8081"</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">ME_CONFIG_MONGODB_ADMINUSERNAME:</span> <span class="hljs-string">admin</span>
      <span class="hljs-attr">ME_CONFIG_MONGODB_ADMINPASSWORD:</span> <span class="hljs-string">password</span>
      <span class="hljs-attr">ME_CONFIG_MONGODB_SERVER:</span> <span class="hljs-string">mongodb</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">mongodb</span>
</code></pre>
<p><strong>Why “my-app” instead of “todo-app”?</strong></p>
<p>In this case, we renamed it to avoid confusion between:</p>
<ul>
<li><p>our <strong>local</strong> “todo-app:1.0”</p>
</li>
<li><p>our <strong>ECR</strong> “todo-app:1.0”</p>
</li>
</ul>
<p>This keeps things clean, but you can rename it back if you want.</p>
<h3 id="heading-why-must-we-use-the-full-image-url-for-ecr">Why Must We Use the Full Image URL for ECR?</h3>
<p>Other containers like mongo and mongo-express work like this:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">image:</span> <span class="hljs-string">mongo</span>
<span class="hljs-attr">image:</span> <span class="hljs-string">mongo-express</span>
</code></pre>
<p>Because Docker knows these are on <strong>Docker Hub</strong>.</p>
<p>But for a private repo like AWS ECR, Docker has no idea where “todo-app” is unless you give the full path:</p>
<pre><code class="lang-yaml"><span class="hljs-string">AWS_ACCOUNT_ID.dkr.ecr.REGION.amazonaws.com/repository_name:tag</span>
</code></pre>
<p>This tells Docker:</p>
<ul>
<li><p>which account</p>
</li>
<li><p>which region</p>
</li>
<li><p>which repo</p>
</li>
<li><p>which version</p>
</li>
</ul>
<p>Without this URL, Docker can’t pull the image.</p>
<p>Every time we want to <em>pull</em> from a private ECR repo, including using Docker Compose, we must be logged in.</p>
<p>Run this:</p>
<pre><code class="lang-yaml"><span class="hljs-string">aws</span> <span class="hljs-string">ecr</span> <span class="hljs-string">get-login-password</span> <span class="hljs-string">--region</span> <span class="hljs-string">eu-north-1</span> <span class="hljs-string">|</span> <span class="hljs-string">docker</span> <span class="hljs-string">login</span> <span class="hljs-string">--username</span> <span class="hljs-string">AWS</span> <span class="hljs-string">--password-stdin</span> <span class="hljs-number">244836489456.</span><span class="hljs-string">dkr.ecr.eu-north-1.amazonaws.com</span>
</code></pre>
<p>If you’re not logged in, Docker Compose will throw:</p>
<p>❌ <code>pull access denied</code><br>❌ <code>repository does not exist</code><br>❌ <code>no basic auth credentials</code></p>
<h3 id="heading-deploy-your-app-using-docker-compose">Deploy Your App Using Docker Compose</h3>
<p>Before deploying, it’s best practice to stop and remove any existing containers to avoid port conflicts or orphaned containers:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Stop all running containers in this project</span>
<span class="hljs-string">docker-compose</span> <span class="hljs-string">down</span> <span class="hljs-string">--remove-orphans</span>

<span class="hljs-comment"># Optional: verify nothing is running</span>
<span class="hljs-string">docker</span> <span class="hljs-string">ps</span>
</code></pre>
<p>This ensures that port 3000 and other mapped ports are free, preventing errors when starting new containers.</p>
<p>Once the environment is clean, deploy your stack:</p>
<pre><code class="lang-yaml"><span class="hljs-string">docker-compose</span> <span class="hljs-string">up</span> <span class="hljs-string">-d</span>
</code></pre>
<p>Docker Compose will:</p>
<ol>
<li><p><strong>Connect to AWS ECR</strong> – Authenticate and pull the <code>todo-app:1.0</code> image from your private repository.</p>
</li>
<li><p><strong>Start MongoDB</strong> – Launch the database container with your configured credentials.</p>
</li>
<li><p><strong>Start Mongo Express</strong> – Launch the web-based MongoDB admin interface.</p>
</li>
<li><p><strong>Start your Node.js app</strong> – Launch the <code>my-app</code> container, linked to MongoDB.</p>
</li>
</ol>
<p>Check the running containers:</p>
<pre><code class="lang-yaml"><span class="hljs-string">docker</span> <span class="hljs-string">ps</span>
</code></pre>
<p>You should see:</p>
<ul>
<li><p><code>mongo</code></p>
</li>
<li><p><code>mongo-express</code></p>
</li>
<li><p><code>my-app</code></p>
</li>
</ul>
<p>If <code>my-app</code> fails to start, it’s usually because <strong>port 3000 is already in use</strong>. Ensure it’s free by stopping any process using it:</p>
<pre><code class="lang-yaml"><span class="hljs-string">lsof</span> <span class="hljs-string">-i</span> <span class="hljs-string">:3000</span>
<span class="hljs-string">kill</span> <span class="hljs-number">-9</span> <span class="hljs-string">&lt;PID&gt;</span>  <span class="hljs-comment"># if a process is using it</span>
</code></pre>
<p>Then rerun:</p>
<pre><code class="lang-yaml"><span class="hljs-string">docker-compose</span> <span class="hljs-string">up</span> <span class="hljs-string">-d</span>
</code></pre>
<p>To access your app:</p>
<ul>
<li><p>Node.js app: <a target="_blank" href="http://localhost:3000"><code>http://localhost:3000</code></a></p>
</li>
<li><p>Mongo Express: <a target="_blank" href="http://localhost:8081"><code>http://localhost:8081</code></a></p>
</li>
</ul>
<p>This workflow ensures a clean start and avoids common port or container conflicts.</p>
<h3 id="heading-sharing-our-private-docker-image">Sharing our Private Docker Image</h3>
<p>Once your Node.js app is pushed to AWS ECR, it’s safely stored in your private repository. But what if another developer, team member, or server needs to run that same image? Since it’s private, Docker cannot pull it automatically like public images (e.g., <code>mongo</code> or <code>nginx</code>). They need <strong>authenticated access</strong>.</p>
<p>Here’s how they can get and use your image:</p>
<h4 id="heading-1-grant-iam-access">1. Grant IAM Access</h4>
<p>Your collaborator needs an <strong>AWS IAM user or role</strong> with permissions for ECR. At minimum, the policy should allow:</p>
<ul>
<li><p><code>ecr:GetAuthorizationToken</code></p>
</li>
<li><p><code>ecr:BatchCheckLayerAvailability</code></p>
</li>
<li><p><code>ecr:GetDownloadUrlForLayer</code></p>
</li>
<li><p><code>ecr:BatchGetImage</code></p>
</li>
</ul>
<p>You can create a dedicated IAM user for this and provide them an Access Key ID and a Secret Access Key.</p>
<h4 id="heading-2-install-and-configure-aws-cli">2. Install and Configure AWS CLI</h4>
<p>The collaborator must have the AWS CLI installed. Then they configure it with their credentials:</p>
<pre><code class="lang-yaml"><span class="hljs-string">aws</span> <span class="hljs-string">configure</span>
</code></pre>
<p>They enter:</p>
<ul>
<li><p>Access Key ID</p>
</li>
<li><p>Secret Access Key</p>
</li>
<li><p>Default region (the same region where the ECR repo exists, for example, <code>eu-north-1</code>)</p>
</li>
<li><p>Default output format (usually <code>json</code>)</p>
</li>
</ul>
<h4 id="heading-3-authenticate-docker-with-ecr">3. Authenticate Docker with ECR</h4>
<p>Before pulling the image, Docker must authenticate using the AWS credentials:</p>
<pre><code class="lang-yaml"><span class="hljs-string">aws</span> <span class="hljs-string">ecr</span> <span class="hljs-string">get-login-password</span> <span class="hljs-string">--region</span> <span class="hljs-string">eu-north-1</span> <span class="hljs-string">|</span> <span class="hljs-string">docker</span> <span class="hljs-string">login</span> <span class="hljs-string">--username</span> <span class="hljs-string">AWS</span> <span class="hljs-string">--password-stdin</span> <span class="hljs-number">244836489456.</span><span class="hljs-string">dkr.ecr.eu-north-1.amazonaws.com</span>
</code></pre>
<p>If successful, Docker will respond with:</p>
<pre><code class="lang-yaml"><span class="hljs-string">Login</span> <span class="hljs-string">Succeeded</span>
</code></pre>
<h4 id="heading-4-pull-the-image">4. Pull the Image</h4>
<p>Now the collaborator can pull the image using the full ECR URI, which includes your AWS account, region, repository name, and tag:</p>
<pre><code class="lang-yaml"><span class="hljs-string">docker</span> <span class="hljs-string">pull</span> <span class="hljs-number">244836489456.</span><span class="hljs-string">dkr.ecr.eu-north-1.amazonaws.com/todo-app:1.0</span>
</code></pre>
<h4 id="heading-5-run-the-container">5. Run the Container</h4>
<p>After pulling, they can run the container locally:</p>
<pre><code class="lang-yaml"><span class="hljs-string">docker</span> <span class="hljs-string">run</span> <span class="hljs-string">-p</span> <span class="hljs-number">3000</span><span class="hljs-string">:3000</span> <span class="hljs-number">244836489456.</span><span class="hljs-string">dkr.ecr.eu-north-1.amazonaws.com/todo-app:1.0</span>
</code></pre>
<p>Or include it in a Docker Compose file, replacing the <code>image:</code> field with the full ECR URI.</p>
<ul>
<li><p>Public images like <code>mongo</code> don’t require this because Docker Hub is open. Private ECR images require explicit authentication.</p>
</li>
<li><p>Every pull from a private repository requires an active login**.** Docker cannot guess credentials.</p>
</li>
<li><p>Using the full image URI ensures Docker knows exactly where to fetch the image.</p>
</li>
</ul>
<p>This setup allows your team to share, deploy, or run your application anywhere, on local machines, staging servers, or production, while keeping your repository private and secure.</p>
<h2 id="heading-docker-volumes">Docker Volumes</h2>
<p>When running containers like MongoDB, all data created inside a container is ephemeral. If the container stops or is removed, all data inside it disappears. This is fine for testing, but not suitable for production.</p>
<p>To solve this, Docker provides <strong>volumes</strong>, which allow containers to store data outside the container, either on the host machine or in Docker-managed storage, so it survives container restarts, rebuilds, or removals.</p>
<h3 id="heading-how-docker-volumes-work">How Docker Volumes Work</h3>
<p>Think of Docker volumes as persistent folders for containers:</p>
<ul>
<li><p>Data written inside a volume remains safe, even if the container is removed.</p>
</li>
<li><p>Containers can read/write to these volumes.</p>
</li>
<li><p>Volumes are essential for databases, logs, file uploads, or any persistent data your application needs.</p>
</li>
</ul>
<h3 id="heading-types-of-docker-volumes">Types of Docker Volumes</h3>
<p>Docker has three main types of volumes:</p>
<h4 id="heading-1-named-volumes">1. Named Volumes</h4>
<p>Named volumes are user-defined volumes with a clear name, that are fully managed by Docker. You’d typically use them in production databases and for persistent data that containers can share.</p>
<p>Here’s an example:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">volumes:</span>
  <span class="hljs-attr">mongo-data:</span>
</code></pre>
<p>And in a service:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">volumes:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">mongo-data:/data/db</span>
</code></pre>
<h4 id="heading-2-bind-mounts">2. Bind Mounts</h4>
<p>Blind mounts map a folder from your <strong>host machine</strong> into the container. They’re often used for development, live syncing files, logs, and uploaded files.</p>
<p>Here’s an example:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">volumes:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">./uploads:/usr/src/app/uploads</span>
</code></pre>
<h4 id="heading-3-anonymous-volumes">3. Anonymous Volumes</h4>
<p>These are volumes without a name. Docker just assigns them a random name. You’d use them for temporary data for testing (and they’re not commonly used in production).</p>
<p>Here’s an example:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">volumes:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">/data/tmp</span>
</code></pre>
<h3 id="heading-example-docker-compose-file-using-volumes">Example Docker Compose File Using Volumes</h3>
<p>Here’s a full <code>docker-compose.yml</code> file using the most common volume types for a Node.js + MongoDB + Mongo Express stack:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">"3.8"</span>

<span class="hljs-attr">services:</span>
  <span class="hljs-attr">my-app:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-number">244836489456.</span><span class="hljs-string">dkr.ecr.eu-north-1.amazonaws.com/todo-app:1.0</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">my-app</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"3000:3000"</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">mongodb</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./uploads:/usr/src/app/uploads</span>  <span class="hljs-comment"># bind mount for file uploads</span>

  <span class="hljs-attr">mongodb:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mongo</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">mongo</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"27017:27017"</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">MONGO_INITDB_ROOT_USERNAME:</span> <span class="hljs-string">admin</span>
      <span class="hljs-attr">MONGO_INITDB_ROOT_PASSWORD:</span> <span class="hljs-string">password</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">mongo-data:/data/db</span>  <span class="hljs-comment"># named volume for persistent database storage</span>

  <span class="hljs-attr">mongo-express:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mongo-express</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">mongo-express</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8081:8081"</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">ME_CONFIG_MONGODB_ADMINUSERNAME:</span> <span class="hljs-string">admin</span>
      <span class="hljs-attr">ME_CONFIG_MONGODB_ADMINPASSWORD:</span> <span class="hljs-string">password</span>
      <span class="hljs-attr">ME_CONFIG_MONGODB_SERVER:</span> <span class="hljs-string">mongodb</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">mongodb</span>

<span class="hljs-attr">volumes:</span>
  <span class="hljs-attr">mongo-data:</span>  <span class="hljs-comment"># named volume definition</span>
</code></pre>
<p>How this code is working:</p>
<ol>
<li><p><strong>MongoDB Volume</strong> (<code>mongo-data</code>): This is a named volume. It stores all database files under <code>/data/db</code> inside the container. It survives container restarts, removals, or rebuilds.</p>
</li>
<li><p><strong>Node.js Uploads (</strong><code>./uploads</code>): This is a <strong>bind mount</strong>. It maps the <code>uploads</code> folder on your host to <code>/usr/src/app/uploads</code> inside the container. Any uploaded files are immediately visible on your host.</p>
</li>
<li><p><strong>Anonymous Volume</strong>: These are not shown in this file because it’s rarely used in production. Temporary data storage is created automatically by Docker if a volume is defined without a name.</p>
</li>
</ol>
<h4 id="heading-visual-concept-simplified">Visual Concept (Simplified):</h4>
<pre><code class="lang-yaml"><span class="hljs-string">Host</span> <span class="hljs-string">Machine</span>
<span class="hljs-string">├─</span> <span class="hljs-string">/project/uploads</span>  <span class="hljs-string">←</span> <span class="hljs-string">bind</span> <span class="hljs-string">mount,</span> <span class="hljs-string">synced</span> <span class="hljs-string">with</span> <span class="hljs-string">container</span>
<span class="hljs-string">├─</span> <span class="hljs-string">Docker</span> <span class="hljs-string">Volumes</span>
<span class="hljs-string">│</span>  <span class="hljs-string">└─</span> <span class="hljs-string">mongo-data</span>    <span class="hljs-string">←</span> <span class="hljs-string">named</span> <span class="hljs-string">volume,</span> <span class="hljs-string">persistent</span> <span class="hljs-string">MongoDB</span> <span class="hljs-string">data</span>

<span class="hljs-string">Containers</span>
<span class="hljs-string">├─</span> <span class="hljs-string">my-app</span>
<span class="hljs-string">│</span>  <span class="hljs-string">└─</span> <span class="hljs-string">/usr/src/app/uploads</span>  <span class="hljs-string">←</span> <span class="hljs-string">sees</span> <span class="hljs-string">host</span> <span class="hljs-string">uploads</span> <span class="hljs-string">folder</span>
<span class="hljs-string">├─</span> <span class="hljs-string">mongodb</span>
<span class="hljs-string">│</span>  <span class="hljs-string">└─</span> <span class="hljs-string">/data/db</span>             <span class="hljs-string">←</span> <span class="hljs-string">uses</span> <span class="hljs-string">named</span> <span class="hljs-string">volume</span> <span class="hljs-string">mongo-data</span>
<span class="hljs-string">├─</span> <span class="hljs-string">mongo-express</span>
</code></pre>
<h3 id="heading-takeaways">Takeaways</h3>
<ul>
<li><p>Always use volumes for data you care about.</p>
</li>
<li><p>Named volumes are best for databases in production.</p>
</li>
<li><p>Bind mounts are best for development and live syncing.</p>
</li>
<li><p>Anonymous volumes are rarely needed outside testing.</p>
</li>
<li><p>Volumes separate container lifecycle from data lifecycle, which is a cornerstone of Docker best practices.</p>
</li>
</ul>
<h3 id="heading-start-your-application">Start Your Application</h3>
<p>Once your Docker Compose is configured with volumes, the next step is to start your application and make sure the volumes are working correctly. Here’s a simple step-by-step guide.</p>
<p><strong>1. Start the Containers</strong></p>
<p>Run:</p>
<pre><code class="lang-yaml"><span class="hljs-string">docker-compose</span> <span class="hljs-string">up</span> <span class="hljs-string">-d</span>
</code></pre>
<p>The <code>-d</code> flag runs the containers in detached mode (in the background).</p>
<p>Docker will:</p>
<ul>
<li><p>Pull your app image from AWS ECR (if you’re logged in)</p>
</li>
<li><p>Start MongoDB with the named volume</p>
</li>
<li><p>Start Mongo Express</p>
</li>
<li><p>Start your Node.js app</p>
</li>
</ul>
<p><strong>2. Check Running Containers</strong></p>
<p>To see if everything started correctly:</p>
<pre><code class="lang-yaml"><span class="hljs-string">docker</span> <span class="hljs-string">ps</span>
</code></pre>
<p>You should see something like:</p>
<pre><code class="lang-yaml"><span class="hljs-string">CONTAINER</span> <span class="hljs-string">ID</span>   <span class="hljs-string">IMAGE</span>                                               <span class="hljs-string">STATUS</span>          <span class="hljs-string">PORTS</span>
<span class="hljs-string">2a2e120cc912</span>   <span class="hljs-number">244836489456.</span><span class="hljs-string">dkr.ecr.eu-north-1.amazonaws.com/todo-app:1.0</span>   <span class="hljs-string">Up</span> <span class="hljs-string">5s</span>    <span class="hljs-number">0.0</span><span class="hljs-number">.0</span><span class="hljs-number">.0</span><span class="hljs-string">:3000-&gt;3000/tcp</span>
<span class="hljs-string">f4d5a1ab1234</span>   <span class="hljs-string">mongo</span>                                               <span class="hljs-string">Up</span> <span class="hljs-string">5s</span>          <span class="hljs-number">0.0</span><span class="hljs-number">.0</span><span class="hljs-number">.0</span><span class="hljs-string">:27017-&gt;27017/tcp</span>
<span class="hljs-string">c3d5b2bc2345</span>   <span class="hljs-string">mongo-express</span>                                      <span class="hljs-string">Up</span> <span class="hljs-string">5s</span>          <span class="hljs-number">0.0</span><span class="hljs-number">.0</span><span class="hljs-number">.0</span><span class="hljs-string">:8081-&gt;8081/tcp</span>
</code></pre>
<p><strong>3. Verify Volumes</strong></p>
<p>List Docker volumes:</p>
<pre><code class="lang-yaml"><span class="hljs-string">docker</span> <span class="hljs-string">volume</span> <span class="hljs-string">ls</span>
</code></pre>
<p>You should see your named volume, for example <code>mongo-data</code>.</p>
<p>Inspect the volume:</p>
<pre><code class="lang-yaml"><span class="hljs-string">docker</span> <span class="hljs-string">volume</span> <span class="hljs-string">inspect</span> <span class="hljs-string">docker_tut_mongo-data</span>
</code></pre>
<p>This will show where Docker stores your MongoDB data on the host, for example:</p>
<pre><code class="lang-yaml">[
    {
        <span class="hljs-attr">"Name":</span> <span class="hljs-string">"mongo-data"</span>,
        <span class="hljs-attr">"Driver":</span> <span class="hljs-string">"local"</span>,
        <span class="hljs-attr">"Mountpoint":</span> <span class="hljs-string">"/var/lib/docker/volumes/mongo-data/_data"</span>,
        <span class="hljs-attr">"Labels":</span> {},
        <span class="hljs-attr">"Scope":</span> <span class="hljs-string">"local"</span>
    }
]
</code></pre>
<p><strong>Anything stored in</strong> <code>/data/db</code> <strong>inside MongoDB is actually saved here on your host.</strong></p>
<p><strong>4. Test Data Persistence</strong></p>
<ol>
<li><p>Connect to MongoDB or your app and add some data.</p>
</li>
<li><p>Stop and remove the container:</p>
</li>
</ol>
<pre><code class="lang-yaml"><span class="hljs-string">docker-compose</span> <span class="hljs-string">down</span>
</code></pre>
<ol start="3">
<li>Restart the app:</li>
</ol>
<pre><code class="lang-yaml"><span class="hljs-string">docker-compose</span> <span class="hljs-string">up</span> <span class="hljs-string">-d</span>
</code></pre>
<ol start="4">
<li>Check your data again.</li>
</ol>
<ul>
<li><p>Because MongoDB uses the named volume, your data is still there.</p>
</li>
<li><p>This proves the volume is persistent.</p>
</li>
</ul>
<p>5. Optional: Check Node.js Uploads (Bind Mount)</p>
<ul>
<li><p>If you uploaded a file through your app, check your project folder <code>./uploads</code>.</p>
</li>
<li><p>You should see the file appear on your host machine because bind mounts sync host and container directories.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Well done, you have made it to the end of this comprehensive Docker tutorial. From unraveling the basics of containers and images, to networking, Docker Compose, volumes, and even deploying to a private AWS ECR repository, you've built a fully containerized Node.js application stack that's production-ready and scalable. These are hands-on skills that will transform how you develop, collaborate, and deploy applications in real-world scenarios.</p>
<p>Thank you for sticking with it. Docker can feel overwhelming at first – those long commands, networking quirks, and persistent data challenges aren't trivial. But getting to this point? It means you've conquered a steep learning curve and reached new heights in your development journey. You're now equipped to eliminate "it works on my machine" headaches, streamline CI/CD pipelines, and level up as a backend or full-stack pro.</p>
<p>Keep experimenting: Tweak your todo-app, try multi-stage builds in your Dockerfile, or explore orchestration tools like Kubernetes next. The Docker ecosystem is vast, but with this foundation, you're ready to dive deeper. If you hit snags or have questions, the community on Docker Hub, Stack Overflow, or GitHub.</p>
<p>You can find the final code here: <a target="_blank" href="https://github.com/Oghenekparobo/docker_tut_js/tree/final">https://github.com/Oghenekparobo/docker_tut_js/tree/final</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Start a Career in Technical Writing by Contributing to Open Source ]]>
                </title>
                <description>
                    <![CDATA[ One of the most common questions I’m asked is, “how can I get started in technical writing?” And honestly, I love that question because it means more people are beginning to see writing as a valid, valuable way to enter the tech industry. Begin with ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/start-a-career-in-technical-writing-through-open-source/</link>
                <guid isPermaLink="false">685594775aea0dba325c37e8</guid>
                
                    <category>
                        <![CDATA[ Technical writing  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technical documentation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Career development  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Open Source ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Oluchi Nwenyi ]]>
                </dc:creator>
                <pubDate>Fri, 20 Jun 2025 17:03:51 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1750439011343/ed2c5552-b132-4edd-8d31-5080daac5aff.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>One of the most common questions I’m asked is, <em>“how can I get started in technical writing?”</em> And honestly, I love that question because it means more people are beginning to see writing as a valid, valuable way to enter the tech industry.</p>
<p><em>Begin with open source.</em> That's what I typically tell them.</p>
<p>It might sound intimidating at first, but open source is one of the most accessible and welcoming ways to learn by doing. You don’t need to be an expert. You don’t need to know how to code extensively. You just need to be willing to write, research, ask questions, and contribute.</p>
<p>In this article, I'll share what I've learnt from my own experience with starting a career in technical writing through open source.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-common-misconceptions-about-technical-writing">Common Misconceptions About Technical Writing</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-who-is-a-technical-writer">Who is a Technical Writer?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-open-source">What is Open Source?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-you-should-build-your-technical-writing-career-with-open-source">Why You Should Build Your Technical Writing Career with Open Source</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-your-role-in-open-source-as-a-technical-writer">Your Role in Open Source as a Technical Writer</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-technical-skills-youll-need-to-get-started">Technical Skills You’ll Need to Get Started</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-a-technical-writing-portfolio-through-open-source">How to Build a Technical Writing Portfolio Through Open Source</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-after-contributing-whats-next">After Contributing, What’s next?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></p>
</li>
</ul>
<h2 id="heading-common-misconceptions-about-technical-writing">Common Misconceptions About Technical Writing</h2>
<p>Before we get into it, let’s get some misconceptions about technical writing out of the way.</p>
<p><strong>“You don’t need to know how to code.”</strong></p>
<p>This is simply not true. While you do not need to be a software engineer to write excellent documentation, understanding basic technical concepts is important, especially if you want to communicate effectively with developers or document complex tools.</p>
<p>That said, many technical writers, myself included, get to learn on the job. You can start with what you know and build your skills from there. The key skill is not necessarily coding – it’s being able to explain things clearly and ask the right questions.</p>
<p><strong>“It’s just about writing things down clearly.”</strong></p>
<p>It’s more than that. Technical writing is about understanding how something works, figuring out what users need to know, and then organising that information in a way that’s useful and accessible.</p>
<p>It involves research, asking questions, testing things out, and often working closely with developers or product teams. The real skill lies in turning complexity into clarity – not just in writing well, but in <em>thinking</em> clearly and structuring information in a way that helps people succeed.</p>
<p><strong>“You need a technical writing degree or certification.”</strong></p>
<p>Also false. While a course or certification can help you build confidence, they’re not required. Many people get started by contributing to open-source documentation, building a portfolio, and learning on the job. Your work speaks for itself, and open source gives you the opportunity to build that body of work publicly.</p>
<p><strong>“Technical writing is boring.”</strong></p>
<p>Not at all. Sure, you’re not writing fiction, but you are solving problems, helping people, and building bridges between developers and users. Every sentence you write could help someone figure something out faster or feel less frustrated. That’s real impact.</p>
<p>Having addressed that, let's proceed.</p>
<h2 id="heading-who-is-a-technical-writer">Who is a Technical Writer?</h2>
<p>Technical writers are the bridge between engineers and everyone else. We make the complex things read more simply, and our work appears in documentation, tutorials, API references, onboarding guides, and more.</p>
<p>As a technical writer, how well you explain things may affect how people use a product, how developers get started, and how whole communities evolve. Good documentation is essential for adoption, and businesses seek writers who can explain technical information with the right empathy and structure.</p>
<p>A good technical writer does more than just write well. They listen, research, and think critically. Some of the core skills you’ll need as a technical writer include:</p>
<ul>
<li><p><strong>Clarity and structure in writing:</strong> Being able to explain things simply and logically.</p>
</li>
<li><p><strong>Curiosity and a willingness to learn:</strong> Asking questions, exploring tools, and digging into unfamiliar concepts.</p>
</li>
<li><p><strong>Empathy for the user:</strong> Understanding what people need and how they think.</p>
</li>
<li><p><strong>Basic technical understanding:</strong> You don’t need to be a programmer, but being comfortable with tools, processes, or basic code reading helps you explain technical concepts better.</p>
</li>
</ul>
<h2 id="heading-what-is-open-source">What is Open Source?</h2>
<p>There are many open-source projects that power tools and technologies you might use every day and many more that power the internet. Projects like Linux, Python, Git, Wikipedia, Firefox, WordPress, and freeCodeCamp are open source projects.</p>
<p>Open source refers to the practice of making a project’s inner workings – whether it’s software, hardware, content, or research – openly available for anyone to view, use, modify, and improve.</p>
<p>Fundamentally, <a target="_blank" href="https://opensource.com/resources/what-open-source">open source</a> rests on several powerful principles:</p>
<ul>
<li><p><strong>Transparency:</strong> Anyone can inspect the project, understand how it works, learn from it, and contribute to it. Nothing is hidden.</p>
</li>
<li><p><strong>Collaboration:</strong> People from all over the world can contribute, suggest improvements, and solve problems together.</p>
</li>
<li><p><strong>Community-driven innovation:</strong> Progress happens through shared effort. The best ideas often emerge when diverse contributors come together.</p>
</li>
</ul>
<p>Open source is about creating things in the open, together.</p>
<h2 id="heading-why-you-should-build-your-technical-writing-career-with-open-source">Why You Should Build Your Technical Writing Career with Open Source</h2>
<p>To get started in technical writing, you need three things: a tool to document, an audience to write for, and a platform to share your work. Open source gives you access to all three and more.</p>
<p>Let’s talk about why contributing to open source is a powerful way to start and grow your technical writing career.</p>
<p>First of all, it can give you some great real-world experience. You get hands-on practice with documentation for actual tools and products used by real developers and users – not just hypothetical or practice projects.</p>
<p>Second, contributing to open source can help you develop your skills. From writing technical guides to improving onboarding flows, you’ll strengthen both your writing and technical understanding.</p>
<p>It also helps you build your portfolio, visibility, and credibility. Your contributions become a tangible way to showcase your skills in writing and working on different types of documentation projects.</p>
<p>Your public work, which is seen by maintainers, contributors, and even recruiters, can also help you build a reputation in the field.</p>
<p>Contributing also helps you develop skills in cross-functional collaboration. You’ll often work alongside developers, designers, and other members of the product team, just like a real job.</p>
<p>As a technical writer, your involvement also helps you gain insights into how they think and develop software, which improves your writing for technical audiences.</p>
<p>In addition to practicing what you already know, there will be many opportunities for you to learn new skills as well. If you’d like to learn new things while being hands-on, open-source is a great place for that. By working on open-source projects, you can gain hands-on experience with new tools, documentation platforms, and even programming languages.</p>
<p>You also get to meet so many people who are willing to mentor, review your work, and offer feedback, so you don’t grow in isolation.</p>
<p>And finally, you get to make a real impact. Good documentation empowers the people you’re writing for. Your writing could be the reason someone else joins a project or starts using a new tool.</p>
<p>You’re not just writing docs. You’re making technology more accessible, communities more welcoming, and learning more equitable.</p>
<h2 id="heading-your-role-in-open-source-as-a-technical-writer">Your Role in Open Source as a Technical Writer</h2>
<p>In open source, technical writers play a crucial role in making projects usable, accessible, and scalable.</p>
<p>As a technical writer, your contributions might include:</p>
<ul>
<li><p><strong>Writing and improving documentation:</strong> From installation guides to API references, you help users get started and stay unblocked.</p>
</li>
<li><p><strong>Creating onboarding resources:</strong> You make it easier for new contributors to join by documenting project structure, workflows, and best practices.</p>
</li>
<li><p><strong>Clarifying technical concepts:</strong> You turn complex ideas into clear, accessible language for users of varying skill levels.</p>
</li>
<li><p><strong>Organising content:</strong> You help structure docs logically so users can find what they need without frustration.</p>
</li>
<li><p><strong>Spotting gaps:</strong> You ask important questions like, 'What’s missing?’ What’s unclear? What would I need to know as a first-time user?</p>
</li>
</ul>
<p>Beyond writing, you become an advocate for the user’s perspective, identifying friction points, promoting clarity, and shaping the overall user experience of a project.</p>
<p>Your role extends beyond mere documentation – it’ll also involve bridging the gap between creators and users and helping open source communities grow by making their work more accessible to everyone.</p>
<h2 id="heading-technical-skills-youll-need-to-get-started">Technical Skills You’ll Need to Get Started</h2>
<p>You don’t need to be a developer to contribute as a technical writer in open source, but having a few foundational skills will make your journey smoother and more impactful.</p>
<p>The core technical skills to focus on include:</p>
<h3 id="heading-git-amp-githubgitlab">Git &amp; GitHub/GitLab</h3>
<p>Version control is essential in open source. Learn how to clone repositories, create branches, make commits and submit pull requests (PRs). These are the basic workflows for contributing to projects and collaborating with others.</p>
<p><a target="_blank" href="https://www.freecodecamp.org/news/learn-git-basics/">Here’s a handbook</a> that’ll teach you all the Git fundamentals you’ll need in your day-to-day routine.</p>
<h3 id="heading-markdown">Markdown</h3>
<p>Markdown is the most common format for writing open source documentation. It’s simple and lightweight, used to add structure like headings, bullet points, code blocks, links, and images to documentation.</p>
<p><a target="_blank" href="https://www.freecodecamp.org/news/markdown-cheatsheet/">Here’s a cheat sheet</a> that’ll teach you markdown basics – it’s geared towards technical writers.</p>
<h3 id="heading-basic-command-line">Basic Command Line</h3>
<p>Knowing how to navigate folders, run simple commands, and install tools from the terminal can help you test software and understand the context you're writing about.</p>
<p>You can learn about command line and bash scripting basics in <a target="_blank" href="https://www.freecodecamp.org/news/bash-scripting-tutorial-linux-shell-script-and-command-line-for-beginners/">this detailed tutorial</a>.</p>
<p><a target="_blank" href="https://www.freecodecamp.org/news/the-linux-commands-handbook/">And here’s a handbook</a> that covers some of the most commonly used Linux commands you’ll need to know.</p>
<h3 id="heading-text-editors">Text Editors</h3>
<p>Tools like VS Code, Cursor, or even online editors on GitHub are commonly used to write and edit documentation. Knowing how to work in them will make your contributions easier.</p>
<p>You can learn how to use VS Code, one of the most popular editors among devs, <a target="_blank" href="https://www.freecodecamp.org/news/learn-visual-studio-code-to-increase-productivity/">in this crash course</a>.</p>
<h3 id="heading-reading-code">Reading Code</h3>
<p>You don’t need to be a programmer, but being able to skim through code and understand file structures or function names can help when documenting how something works.</p>
<p>You don’t need to master everything before you start. These are skills you’ll need to develop along the way. The most important thing is to be curious, ask questions, and keep learning as you contribute.</p>
<h2 id="heading-how-to-build-a-technical-writing-portfolio-through-open-source">How to Build a Technical Writing Portfolio Through Open Source</h2>
<p>Open source is one of the most effective ways to build a strong, public portfolio and kickstart your career as a technical writer. Here’s a step-by-step guide to help you begin contributing to open source as a technical writer:</p>
<h3 id="heading-create-a-github-and-optionally-gitlab-account">Create a GitHub (and optionally GitLab) account</h3>
<p>Most open source projects are hosted on GitHub, so this is your gateway to finding and contributing to them. Make sure your profile is complete and professional – consider it to be your public résumé.</p>
<p>Here’s a peek at what a good GitHub profile can look like (it’s mine 🤗), with a clear bio, current work, interests, and ways to connect. You don’t need to overthink it – just make sure it reflects who you are and what you’re working on.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750356311009/27fe7266-3dc2-4126-af07-a246f31443ea.png" alt="My GitHub profile" class="image--center mx-auto" width="1364" height="765" loading="lazy"></p>
<h3 id="heading-explore-documentation-focused-opportunities">Explore documentation-focused opportunities</h3>
<p>Look for repositories tagged with labels like <code>documentation</code>, <code>good-first-issue</code>, or <code>help wanted</code>. These typically signal that certain tasks (issues) are accessible to new contributors and often related to improving or creating documentation.</p>
<p>Some examples of open-source projects that are known to be welcoming to documentation contributors include <a target="_blank" href="https://github.com/mautic/low-no-code/issues">Mautic</a>, <a target="_blank" href="https://github.com/github/docs">GitHub</a>, <a target="_blank" href="https://github.com/search?q=org:mozilla+doc&amp;type=repositories">Mozilla</a>, <a target="_blank" href="https://github.com/layer5io/layer5">Layer5</a>, <a target="_blank" href="https://github.com/kubernetes/website/issues?q=is:issue%20state:open%20label:kind/documentation">Kubernetes</a>, etc.</p>
<p>You can also browse communities like <a target="_blank" href="https://hashnode.com/">Hashnode</a>, <a target="_blank" href="https://dev.to/">Dev.to</a>, or Reddit’s <a target="_blank" href="https://reddit.com/r/opensource">r/opensource</a> for leads on projects seeking writing support.</p>
<h3 id="heading-start-with-simple-contributions">Start with simple contributions</h3>
<p>Ease into the process by tackling small but valuable tasks like fixing typos or grammar errors, enhancing clarity and structure, and improving formatting or consistency.</p>
<p>These types of edits help you learn the contribution workflow and gain confidence. As you grow more comfortable, take on larger tasks, like writing tutorials, onboarding guides, or reorganising documentation systems.</p>
<h3 id="heading-engage-with-project-maintainers">Engage with project maintainers</h3>
<p>Before making changes, introduce yourself by opening an issue or commenting on an existing one. Ask if you can work on a specific improvement, and clarify expectations around style, tone, or structure.</p>
<p>Good communication shows respect for the project and increases the likelihood your contribution will be accepted.</p>
<h3 id="heading-document-your-work-as-you-go">Document your work as you go</h3>
<p>For every contribution you make, keep track of it. Create a personal documentation portfolio with links to pull requests or merged contributions, screenshots or before-and-after comparisons, and explanations of what you improved and why.</p>
<p>This portfolio serves as compelling evidence of your skills and impact, which can be beneficial for job applications, freelance work, or community recognition.</p>
<h2 id="heading-after-contributing-whats-next">After Contributing, What’s next?</h2>
<p>Once you’ve started making contributions, you’re no longer “aspiring” – you’re already doing the work. That’s something to celebrate. But it’s also just the beginning. Here’s how to keep the momentum going and turn your contributions into a foundation for growth:</p>
<h3 id="heading-reflect-on-what-youve-learned"><strong>Reflect on what you’ve learned</strong></h3>
<p>After my first few open source contributions (fixing some formatting and clarifying contributing guides), I realized how much I learned just by navigating the repo, reading through issues, and figuring out how to submit a PR.</p>
<p>I wasn’t just improving docs. I was learning Git and understanding community norms. Ask yourself: What did you enjoy? What was tricky? What would you do differently next time?</p>
<h3 id="heading-ask-for-feedback"><strong>Ask for feedback</strong></h3>
<p>Don’t be afraid to reach out to maintainers or fellow contributors. Thoughtful feedback can sharpen your writing, improve your collaboration skills, and help you grow faster.</p>
<p>I often left comments like: <em>“Hey! I’ve submitted a small change to improve clarity in this section. I’d love your feedback on whether the tone and structure align with your existing style guide.”</em></p>
<p>Every time I did, I either got affirmation, or helpful advice, and both helped me grow faster.</p>
<h3 id="heading-stay-involved-in-the-community"><strong>Stay involved in the community</strong></h3>
<p>Contributing once is wonderful. But staying active, participating in discussions, joining meetings, or helping other newcomers deepens your understanding and strengthens your place in the project.</p>
<h3 id="heading-contribute-again-and-again"><strong>Contribute again (and again)</strong></h3>
<p>Look for other areas in the same project or explore new ones. As you gain experience, try tackling more complex documentation tasks or helping shape the docs strategy.</p>
<h3 id="heading-share-your-journey"><strong>Share your journey</strong></h3>
<p>Document your experience through blog posts, tweets, or videos. Sharing showcases your work and inspires others to contribute.</p>
<p>I’ve written blog posts and Twitter threads reflecting on my learning process, not just to celebrate but to document what I did and help others who are a few steps behind me. Almost every time I published something on learning about open-source, someone reached out to say it helped them or ask questions.</p>
<p>You might be the nudge someone needs to start contributing too.</p>
<h3 id="heading-join-writing-communities"><strong>Join writing communities</strong></h3>
<p>Get connected with other writers through groups like <a target="_blank" href="https://www.writethedocs.org/">Write the Docs</a>, the <a target="_blank" href="http://hackmamba.io">Hackmamba</a> community, or even local writing meetups. These communities offer support, resources, and new opportunities.</p>
<h3 id="heading-turn-it-into-a-career"><strong>Turn it into a career</strong></h3>
<p>Use your contributions as a portfolio to apply for internships, fellowships, or junior tech writing roles. Pitch your writing to developer-focused blogs or publications like freeCodeCamp. And start freelance work for startups or open-source organisations.</p>
<p>Technical writing has so many paths – full-time roles, freelancing, developer education, content strategy, documentation engineering – and the work you do in open source can lead to any of them.</p>
<h3 id="heading-keep-learning"><strong>Keep learning</strong></h3>
<p>Technical writing is a skill you can continue to refine through courses, peer reviews, mentorship, and hands-on practice. You can explore new tools or pick up a little code; do whatever helps you grow.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>Your first contribution proves that you can write for real-world tools, collaborate in technical spaces, and add value to communities. What comes next is up to you, but you’ve already taken the most important step: you’ve started.</p>
<p><strong>Have questions or want to share your journey?</strong><br>I’d love to hear from you. Feel free to connect or reach out to me on <a target="_blank" href="https://www.linkedin.com/in/lulunwenyi">LinkedIn</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Write Documentation That Increases Sign-ups ]]>
                </title>
                <description>
                    <![CDATA[ Writing documentation looks easy, but it is one of the most critical parts of your customer support and growth strategy. Too often, teams treat it as an afterthought – just add a few code snippets and move on. But if you’re serious about product adop... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-write-documentation-that-increases-sign-ups/</link>
                <guid isPermaLink="false">68542e3585d4833f32068976</guid>
                
                    <category>
                        <![CDATA[ documentation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Technical writing  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tooba Jamal ]]>
                </dc:creator>
                <pubDate>Thu, 19 Jun 2025 15:35:17 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1750347297168/f873d57d-22e5-4cbb-bda9-ca24861db09d.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Writing documentation looks easy, but it is one of the most critical parts of your customer support and growth strategy. Too often, teams treat it as an afterthought – just add a few code snippets and move on.</p>
<p>But if you’re serious about product adoption, your docs need to do more than exist. They need to guide, support, and convert. In this tutorial, we’ll break down how to write documentation that actually helps users and gets them to stick around.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-whats-the-problem-with-most-documentation">What’s the Problem with Most Documentation?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-write-documentation-from-the-users-perspective">How to Write Documentation from the User’s Perspective</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-start-by-understanding-your-users">Start by Understanding Your Users</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-structure-around-the-four-documentation-types">Structure Around the Four Documentation Types</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-make-it-easy-to-reach-out">Make it Easy to Reach Out</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-keep-your-tech-team-in-the-loop">Keep Your Tech Team in the Loop</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-reuse-what-youve-already-written">Reuse What You’ve Already Written</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-get-ai-help-the-smart-way">Get AI Help (The Smart Way)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-get-feedback-early">Get Feedback Early</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-does-documentation-increase-sign-ups">How Does Documentation Increase Sign-ups?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-making-complex-documentation-accessible">Making Complex Documentation Accessible</a></p>
</li>
</ul>
<h2 id="heading-whats-the-problem-with-most-documentation">What’s the Problem with Most Documentation?</h2>
<p>The most common problem with documentation is that its authors believe their audience knows everything. This leads to the loss of many potential leads – causing people to abandon your platform before even fully understanding it.</p>
<p>Here’s why:</p>
<ul>
<li><p>It’s often dry and code-heavy, with snippets and vague bullet points.</p>
</li>
<li><p>It dumps information and expects the readers to figure it out.</p>
</li>
<li><p>It’s written from the developer’s point of view, not the user’s.</p>
</li>
<li><p>It rarely shows how the product fits into the user’s specific use case or workflow.</p>
</li>
<li><p>It combines different content types into a chaotic mess that’s difficult to follow or navigate.</p>
</li>
</ul>
<h2 id="heading-how-to-write-documentation-from-the-users-perspective">How to Write Documentation from the User’s Perspective</h2>
<p>Most documentation is written from a developer’s perspective: technical, feature-first, and dry. But at the end of the day, developers are also humans with problems like others, and when browsing your documentation, they are asking:</p>
<ul>
<li><p>“Can this solve my problem?”</p>
</li>
<li><p>“How do I get it working for my use case?”</p>
</li>
<li><p>“What happens if I get stuck?”</p>
</li>
</ul>
<p>Here’s how you can make people stay and come back to your documentation.</p>
<h3 id="heading-start-by-understanding-your-users">Start by Understanding Your Users</h3>
<p>Before writing a single line of documentation, pause and ask: What do our users actually care about? And it's more than just “connect an API to their client’s app”. It's the intangible benefits they’re seeking, such as two hours saved due to your tool while connecting the API or collecting payments from different clients with a single workflow.</p>
<p>This means that you need to listen before you write.</p>
<p>Dig into conversations happening around your product, not just within it. What questions do users consistently ask during sales calls? What pain points come up in Reddit threads or support tickets? What complaints are circulating on social media that haven’t been addressed yet?</p>
<p>Pay close attention to the words they use while referring to your product.</p>
<p>When you use their words, not yours, your documentation starts to sound like a helpful guide, not a technical manual. That shift alone can be the difference between “let me try this tool” and “I’ll figure it out later.”</p>
<p>Supabase is a good example of user-friendly docs. It begins by categorizing content by product, module, and client library. Each category includes comprehensive documentation with real-world examples, practical use cases, tutorials, and built-in feedback support.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750260785373/46bdc0af-3de8-42c3-bd36-02390db5df2d.png" alt="Screenshot from Supabase's docs" class="image--center mx-auto" width="1897" height="902" loading="lazy"></p>
<h3 id="heading-structure-around-the-four-documentation-types">Structure Around the Four Documentation Types</h3>
<p>Now that you know your customer, your job is to guide them to the correct answer fast. That’s hard to do if everything’s thrown together in one long page. Structure is what turns chaos into clarity.</p>
<p>The best documentation systems categorize information into four types, a <a target="_blank" href="https://docs.divio.com/documentation-system/">framework</a> originally proposed by Divio. Not just different sections, but completely different purposes. Each one meets your user at a different stage of their journey.</p>
<p>Let’s break them down:</p>
<h4 id="heading-1-quickstart">1. Quickstart</h4>
<p>This is the user’s first real experience with your product, and it’s where they decide whether to continue using it. So your job here isn’t to explain everything. It’s to walk them through one thing that works. Slowly. Clearly. And in their language.</p>
<p>Use examples. Don’t assume prior knowledge. Avoid jargon unless you’ve already explained it. The goal is to create that first “Oh, I get it now” moment. Because once they’ve had it, they’ll stick around for more.</p>
<p>For example, the <a target="_blank" href="https://react.dev/learn">React documentation</a> gives beginners a simple walkthrough, from creating a component to passing data between them. It avoids jargon, focuses on one working example, and explains each step clearly. This helps users quickly grasp how React works, creating that first “Oh, I get it now” moment.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750243332841/7a1f98f2-24b1-400c-aadd-0534419b0379.png" alt="Screenshot of the React docs" class="image--center mx-auto" width="1895" height="853" loading="lazy"></p>
<h4 id="heading-2-how-to-guides">2. How-to Guides</h4>
<p>These are for users who already know the basics and just want to solve a problem. “How do I connect this to Slack?” “How do I export this as a CSV?” They’re not here to learn concepts. They just want to follow instructions and get something done.</p>
<p>Start by telling them exactly what the guide will help them do. List prerequisites up front if needed. Then walk them through the steps.</p>
<p>For example, the <a target="_blank" href="https://www.tensorflow.org/tutorials/images/cnn">TensorFlow documentation</a> includes a step-by-step guide on how to build a Convolutional Neural Network (CNN) to classify images. It’s task-focused and practical, so anyone trying to implement image classification with TensorFlow knows exactly where to go and what steps to follow.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750243604478/992a12b3-12fb-4c17-a5ec-713e67a49bae.png" alt="Screenshot from the TensorFlow docs" class="image--center mx-auto" width="1896" height="907" loading="lazy"></p>
<h4 id="heading-3-technical-reference">3. Technical Reference</h4>
<p>This is where the raw details live. Your endpoints, CLI commands, Parameter lists, what happens when something fails, and so on. Think of it like a glossary of your tool.</p>
<p>People scan this portion of your documentation, so make it easy to scan. Organize it so someone can jump to what they need. Use consistent formatting and avoid explanations here, but add links to the explanations sections.</p>
<p>For example, the <a target="_blank" href="https://kubernetes.io/docs/home/">Kubernetes documentation</a> includes a comprehensive API reference that lists all available resources, their fields, default values, and behaviors. It’s organized into categories like common parameters and workload resources, making it easy to navigate. Each section focuses on definitions and parameters, with links to related conceptual docs for deeper context.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750244073207/d6c7fddb-8bac-4c25-9fad-b83f2bf8fc72.png" alt="Screenshot from the Kubernetes docs" class="image--center mx-auto" width="1896" height="902" loading="lazy"></p>
<h4 id="heading-4-explanations">4. Explanations</h4>
<p>This is where you step back and explain the thinking behind your system. Why does your product rely on webhooks? Why did you structure your SDK a certain way? When should someone use one method over another?</p>
<p>This part differentiates your tool from the crowd and builds trust. Yet, many documentations miss this. Don’t forget to add a separate section on common errors beyond 404s here. The errors that people encounter when using your tool provide a straightforward solution to those errors.</p>
<p>For example, <a target="_blank" href="https://docs.divio.com/">Divio</a> has an explanation section in its documentation that covers each relevant concept in detail. It starts by explaining the concept itself, then shows how it works within Divio and what practical applications it has. This helps users understand the reasoning behind the system and how to use it effectively.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750244241171/f37e28f5-7c8b-4a2d-bf9b-bd99edf6fa9e.png" alt="Screenshot from the Divio docs" class="image--center mx-auto" width="1893" height="901" loading="lazy"></p>
<h3 id="heading-make-it-easy-to-reach-out">Make it Easy to Reach Out</h3>
<p>Even with perfect docs, users will get stuck. And the worst thing you can do now is make them hunt for help.</p>
<p>Make it ridiculously easy to reach out.</p>
<p>Add a simple “Was this helpful?” prompt at the end of each page. If the answer is no, give them a quick way to say why.</p>
<p>Drop in a link to report bugs, outdated steps, or confusing content. You’ll get real-time feedback from real users.</p>
<p>Invite them to contact support or drop a question in your community Slack or Discord. It’s not just about solving issues. It’s about showing them you’re listening.</p>
<h3 id="heading-keep-your-tech-team-in-the-loop">Keep Your Tech Team in the Loop</h3>
<p>You don’t need to know how every line of code works. But you <em>do</em> need a clear path to someone who does.</p>
<p>Set up a shared document, Notion page, or Slack thread where your development team can casually share technical decisions, temporary errors, important workarounds, and other relevant information. </p>
<p>Also, don’t be afraid to ask your devs directly. A 3-minute voice note or quick message like “Does this sound right to you?” can prevent a dozen user errors later..</p>
<h3 id="heading-reuse-what-youve-already-written">Reuse What You’ve Already Written</h3>
<p>Chances are, you've already explained 80% of what needs to go in your docs you just didn’t call it “documentation” at the time.</p>
<p>Start by digging through what’s already there:</p>
<ul>
<li><p>Sales or support Slack threads where you broke things down clearly</p>
</li>
<li><p>Onboarding emails that walk users through the first steps</p>
</li>
<li><p>Internal guides your team relies on</p>
</li>
<li><p>Blog posts where you’ve explained product decisions</p>
</li>
<li><p>FAQs your support team sends on repeat</p>
</li>
</ul>
<p>Copy-paste, trim, reframe, and you’ve got a doc draft in minutes.</p>
<h3 id="heading-get-ai-help-the-smart-way">Get AI Help (The Smart Way)</h3>
<p>AI tools can cut your documentation time in half, but only if you treat them like a collaborator. One powerful way to use them is by turning raw code into structured docs. </p>
<p>Just drop in a code snippet or feature file and prompt something like: “Write a reference doc for this API endpoint. Include usage, parameters, response structure, and a working example.” </p>
<p>Then take it a step further: “Now rewrite this for a non-technical product manager who’s using the tool for the first time.” This way, you get both the technical version and a plain-English explanation without switching tabs ten times.</p>
<p>I used AI to generate the documentation for the <code>user.updated</code> webhook event, and here’s what it came up with. While the generated doc is functional, I’d like to improve it further.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750261767771/e9db912c-5a3c-47e9-82cb-6661b4506e92.png" alt="e9db912c-5a3c-47e9-82cb-6661b4506e92" class="image--center mx-auto" width="995" height="805" loading="lazy"></p>
<p>Here are my initial thoughts:</p>
<ul>
<li><p>Avoid starting with generic phrases like “This document describes...”</p>
</li>
<li><p>Jump straight into what the <code>user.updated</code> event does</p>
</li>
<li><p>Remove unnecessary passive voice to improve clarity</p>
</li>
<li><p><strong>For example, instead of:</strong><br>  <em>"The</em> <code>user.updated</code> event is triggered when a user's profile is updated in the system."<br>  <strong>use:</strong><br>  <em>"Whenever a user profile is updated in your system, the</em> <code>user.updated</code> event is triggered."</p>
</li>
</ul>
<p>If the generated text sounds robotic, you can use an <a target="_blank" href="https://humanizerpro.ai/">AI humanizer</a> to make it more natural. Then, give it a final review to remove unnecessary content, add your voice, and ensure accuracy.</p>
<h3 id="heading-get-feedback-early">Get Feedback Early</h3>
<p>Don’t wait for the “final version” before asking for input. Documentation needs to be useful, and it’s better to get clarity early in the process. When you ask real humans for feedback on your documentation, you get a reality check before a customer spreads negative feedback about you.</p>
<p>Instead, test your docs in real-world conversations:</p>
<ul>
<li><p>Drop a how-to guide in your support team’s Slack and ask, “Would you send this to a user?”</p>
</li>
<li><p>Ask a teammate or friend to follow your tutorial. Watch where they pause or get confused.</p>
</li>
<li><p>Run it by sales or customer success, they know exactly where users get stuck in onboarding.</p>
</li>
</ul>
<p>And if you're just a small team of two people, ask each other. Then ask each other's friends. You don’t need a formal process, just honest reactions from real people.</p>
<h2 id="heading-how-does-documentation-increase-sign-ups">How Does Documentation Increase Sign-ups?</h2>
<p>When a developer first arrives at your documentation page, they give it a few minutes to decide whether your tool is worth using, especially if they’re still exploring options.</p>
<p>If they’re required to use your tool for any reason, they’ll likely spend a day or two trying to figure things out. If they can’t find the right information, the frustration builds and often escalates to upper management. That’s when you risk not just losing a user, but losing a team or even a larger client.</p>
<p>Good documentation prevents that.</p>
<p>It is well-structured, speaks to users at all levels, and is easy to navigate. It keeps users on your page longer and encourages them to test your tool instead of bouncing to a competitor. If you also offer feedback channels or live support, users are more likely to reach out when they get stuck, and that support interaction can make all the difference.</p>
<p>Good documentation:</p>
<ul>
<li><p>Reduces support dependency, so users spend more time building and less time troubleshooting</p>
</li>
<li><p>Lowers frustration, which signals maturity and product quality</p>
</li>
<li><p>Builds trust by showing that your team has thought through user needs</p>
</li>
<li><p>Helps users get something working quickly, which often turns into a sign-up</p>
</li>
</ul>
<h2 id="heading-making-complex-documentation-accessible">Making Complex Documentation Accessible</h2>
<p>Writing good docs requires careful planning, collaboration, and a solid understanding of your users. Whether your developers are writing it or you're working with a technical copywriter, excellent documentation is rarely a solo job. It requires input from the people who developed the product and those who support its users daily.</p>
<p>When done right, your docs don’t just explain things – they make your product more straightforward to use, build trust with potential customers, and drive sign-ups.</p>
<p>Liked what you read? I help SaaS companies clarify their messaging and build authority through content. You can connect with me on <a target="_blank" href="https://www.linkedin.com/in/tooba-jamal/">LinkedIn</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn TypeScript – A Handbook for Developers ]]>
                </title>
                <description>
                    <![CDATA[ This handbook will teach you the basics of TypeScript, including what it is, why it is useful, and the key features it offers. TypeScript was created by Anders Hejlsberg, a prominent software engineer at Microsoft who’s also known for his work on C# ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-typescript-with-react-handbook/</link>
                <guid isPermaLink="false">67a62638dd67e262dbd96da1</guid>
                
                    <category>
                        <![CDATA[ TypeScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Programming Blogs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Technical writing  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ handbook ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Oghenekparobo Stephen ]]>
                </dc:creator>
                <pubDate>Fri, 07 Feb 2025 15:26:48 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1738941922431/cfb485ae-1c59-415a-ad56-393a9803d4d8.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>This handbook will teach you the basics of TypeScript, including what it is, why it is useful, and the key features it offers.</p>
<p>TypeScript was created by Anders Hejlsberg, a prominent software engineer at Microsoft who’s also known for his work on C# and Delphi. TypeScript was designed to enhance JavaScript by adding static types, making it easier to build and maintain large-scale applications.</p>
<p>We’ll start by using Vite to integrate TypeScript with a React project. Then you’ll learn about key concepts like type annotations, type inference, and how to handle objects and arrays.</p>
<p>After that, we’ll explore advanced topics such as union and any types, readonly properties, functions with specific parameter and return types, generics for flexible and reusable code, and the distinctive roles of type aliases and interfaces.</p>
<p>I’ll provide detailed examples and explanations throughout the handbook to give you a comprehensive understanding of how TypeScript's features can improve JavaScript development.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>I assume you are already familiar with the fundamentals of JavaScript and React. So in this handbook, I won’t be going into in-depth explanations of certain concepts, such as the file structure when scaffolding projects.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a target="_blank" href="heading-what-is-typescript">What is TypeScript?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-setting-up-the-project">Setting Up the Project</a></p>
</li>
<li><p><a target="_blank" href="heading-type-annotations-and-type-inference">Type Annotations and Type Inference</a></p>
<ul>
<li><p><a target="_blank" href="heading-commonly-used-type-annotations">Commonly Used Type Annotations</a></p>
</li>
<li><p><a target="_blank" href="heading-type-inference">Type Inference</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-the-union-and-any-types">The Union and Any Types</a></p>
<ul>
<li><p><a target="_blank" href="heading-be-careful-when-using-any-in-typescript">Be Careful When Using any in TypeScript</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-unknown-as-a-safer-alternative-to-any-in-typescript">Using unknown as a Safer Alternative to any in TypeScript</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-objects-in-typescript">Objects in TypeScript</a></p>
<ul>
<li><p><a target="_blank" href="heading-the-problem-of-mutability">The Problem of Mutability</a></p>
</li>
<li><p><a target="_blank" href="heading-readonly-on-object-properties">Readonly on Object Properties</a></p>
</li>
<li><p><a target="_blank" href="heading-readonly-arrays">Readonly Arrays</a></p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="heading-function-params-and-function-returns">Function Params And Function Returns</a></p>
<ul>
<li><p><a target="_blank" href="heading-the-risks-of-using-any">The Risks of Using any</a></p>
</li>
<li><p><a target="_blank" href="heading-use-explicit-types-for-parameters-and-return-values">Use Explicit Types for Parameters and Return Values</a></p>
</li>
<li><p><a target="_blank" href="heading-using-unknown-as-a-safer-alternative-to-any-in-typescript">Using unknown as a Safer Alternative to any in TypeScript</a></p>
</li>
<li><p><a target="_blank" href="heading-handling-optional-default-in-typescript">Handling Optional, Default in TypeScript</a></p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="heading-rest-parameters">Rest Parameters</a></p>
</li>
<li><p><a target="_blank" href="heading-objects-as-parameters-in-typescript">Objects as Parameters in TypeScript</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-type-aliases-in-typescript">Type Aliases in TypeScript</a></p>
<ul>
<li><a target="_blank" href="heading-what-is-an-intersection-type-in-typescript">What is an Intersection Type in TypeScript?</a></li>
</ul>
</li>
<li><p><a target="_blank" href="heading-interfaces-in-typescript">Interfaces in TypeScript</a></p>
</li>
<li><p><a target="_blank" href="heading-tuples-and-enums">Tuples and Enums</a></p>
</li>
<li><p><a target="_blank" href="heading-type-assertion-type-unknown-and-type-never-in-typescript">Type Assertion, Type Unknown, and Type Never in TypeScript</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-generics-in-typescript">Generics in TypeScript</a></p>
</li>
<li><p><a target="_blank" href="heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-what-is-typescript">What is TypeScript?</h2>
<p>Before diving into what TypeScript is, it's important to understand why it was created. JavaScript is a <strong>loosely typed language</strong>, meaning variables are defined and their types are determined at runtime. This flexibility can lead to unexpected behavior, especially in larger projects.</p>
<p>For example, you might accidentally assign a value of the wrong type to a variable, resulting in errors that you only discover when the code is executed.</p>
<p>Here’s an example of JavaScript that demonstrates this issue:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> userName = <span class="hljs-string">"Alice"</span>;
userName = <span class="hljs-number">42</span>; <span class="hljs-comment">// No error during assignment, but this might break the code later.</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">greetUser</span>(<span class="hljs-params">name</span>) </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Hello, "</span> + name.toUpperCase()); <span class="hljs-comment">// Error at runtime if `name` is not a string.</span>
}

greetUser(userName); <span class="hljs-comment">// Throws an error because `userName` is a number, not a string.</span>
</code></pre>
<p>This error can be challenging to debug, as it only surfaces at runtime, making large projects harder to maintain and more prone to bugs.</p>
<p>This is where TypeScript comes into the picture. TypeScript is a programming language that builds on JavaScript by adding <strong>static typing</strong>. Static typing means you can explicitly specify the types of variables, function arguments, return values, and more. Unlike <strong>dynamic typing</strong>, where types are determined at runtime, static typing allows TypeScript to catch type-related errors early during development, improving code quality and reducing bugs.</p>
<p>For example, here’s the same code written in TypeScript:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> userName: string = <span class="hljs-string">"Alice"</span>;
<span class="hljs-comment">// userName = 42; // Error: Type 'number' is not assignable to type 'string'.</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">greetUser</span>(<span class="hljs-params">name: string</span>): <span class="hljs-title">void</span> </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Hello, "</span> + name.toUpperCase());
}

greetUser(userName); <span class="hljs-comment">// Works perfectly since `userName` is correctly typed.</span>
</code></pre>
<h2 id="heading-setting-up-the-project">Setting Up the Project</h2>
<p>We will be using <a target="_blank" href="https://vite.dev/guide/">Vite</a> to set up our TypeScript project. Vite is a modern build tool designed to offer a faster and leaner development experience for web projects.</p>
<p>To get started, run the following command to create a new Vite project with TypeScript support:</p>
<pre><code class="lang-javascript">npm create vite@latest
</code></pre>
<p>Then enter a name for your project (you may choose any name you prefer). Follow the instructions carefully in the subsequent steps</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736769678848/93e22821-6044-4b06-b5ba-86cd3f01ca98.png" alt="creating a project with npm create vite@latest" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Select your project template by choosing ‘React’ from the available options. We will be using React with TypeScript for this project's development.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736769912180/e94dc70c-32e2-4f9f-89cc-d70d35e3a86e.png" alt="project template when you run, create vite@latest" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>When prompted for a variant selection, choose 'TypeScript' from the available options.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736770059262/d605726e-8d4f-4e73-8fb7-3854ce0b4e72.png" alt="variant selection of typescript, in create vite@latest template" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>After completing these steps, you will be prompted to navigate to your project directory and run <code>npm install</code>. You can use any code editor of your choice. For this example, I will be using VS Code.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736771043869/e3f81f8b-19b7-4fb6-a439-2f24e3f55df5.png" alt="e3f81f8b-19b7-4fb6-a439-2f24e3f55df5" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736771426441/4c524149-4557-40bf-b50a-79400c6c3c91.png" alt="overview of your project in vscode and running npm install to install project dependencies" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>After running <code>npm install</code>, run <code>npm run dev</code> to start the project on the local server. Once that’s up and running, we are ready to dive into learning TypeScript concepts.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736772238962/36f9523c-d316-43e3-ae05-e1ebfa9398f1.png" alt="our landing page after running npm run dev in our project" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>But first, let's create our first TypeScript file, <code>test.ts</code> (you can choose to use <code>.ts</code> or <code>.tsx</code>). Create the <code>test.ts</code> file inside the <code>src</code> folder of your project, and add the following code to log a test message:</p>
<p><code>test.ts</code></p>
<pre><code class="lang-javascript"><span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Testing our first TypeScript file'</span>);
</code></pre>
<p>To view this in the console, import the <code>test.ts</code> file into the <code>main.tsx</code> file located in the <code>src</code> folder.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736773745661/8492e586-7bc0-44a8-ac54-fb576119cdea.png" alt="highlighting the main.tsx and test.tsx file" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p><code>main.tsx</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { StrictMode } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { createRoot } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-dom/client"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./index.css"</span>;
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">"./App.tsx"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./test.ts"</span>;

createRoot(<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"root"</span>)!).render(
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">StrictMode</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">StrictMode</span>&gt;</span></span>
);
</code></pre>
<p>To view the log in the console, make sure to import the <code>test.ts</code> file into the <code>main.tsx</code> file located in the <code>src</code> folder. After that, check the console of your project running on the local server, and you should see the logged message displayed there.</p>
<p><strong>Voilà!</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736774231199/9a270631-0639-40e0-84de-513143b4478d.png" alt="our result in console.log" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Now let’s get down to the real business of learning TypeScript.</p>
<h2 id="heading-type-annotations-and-type-inference">Type Annotations and Type Inference</h2>
<h3 id="heading-what-are-type-annotations">What are Type Annotations?</h3>
<p>Type annotations in TypeScript enable you to explicitly specify the type of a variable. This ensures that the variable is assigned only values of the specified type, enhancing type safety and making your code easier to maintain.</p>
<p>To define a type annotation in TypeScript, you simply append a colon <code>:</code> followed by the desired type after the variable name. This allows you to specify the type that a variable will hold, adding a layer of clarity and precision to your code. For instance, let’s specify a variable of type <code>string</code> in our <code>test.ts</code> file, ensuring that only a string value is assigned:</p>
<p><code>test.ts</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> name: string = <span class="hljs-string">'Stephen'</span>;
</code></pre>
<p>In this example, we have declared a variable <code>name</code> and specified that its type must be <code>string</code>. TypeScript will now ensure that only a string value can be assigned to <code>name</code>.</p>
<p><strong>📄 Note:</strong> All code snippets are in a file called <code>test.ts</code> for demonstration purposes. You can rename the file or copy the snippets into your TypeScript project as needed. I don’t follow consistent file naming in this article.</p>
<h3 id="heading-commonly-used-type-annotations">Commonly Used Type Annotations</h3>
<p>Here are some of the most commonly used type annotations in TypeScript:</p>
<ul>
<li><p><code>string</code>: Represents text values.</p>
</li>
<li><p><code>number</code>: Represents numeric values (both integers and floating-point numbers).</p>
</li>
<li><p><code>boolean</code>: Represents a value that is either <code>true</code> or <code>false</code>.</p>
</li>
<li><p><code>any</code>: A fallback type that allows any value to be assigned to a variable, disabling type checking.</p>
</li>
<li><p><code>void</code>: Typically used for functions that do not return a value.</p>
</li>
<li><p><code>null</code> and <code>undefined</code>: Used to represent the absence of a value.</p>
</li>
</ul>
<p>Once you define a variable with a type annotation, TypeScript ensures that it can only hold values of that specified type. You can also access the methods associated with that type. For example, if you declare a string variable, TypeScript provides access to all string methods, such as <code>.toUpperCase()</code>.</p>
<p><code>test.ts</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> name: string = <span class="hljs-string">'Stephen'</span>;  <span class="hljs-comment">// Type is explicitly set as string</span>
name = <span class="hljs-string">'John'</span>;  <span class="hljs-comment">// This is fine, as it's still a string</span>

<span class="hljs-comment">// Accessing string method</span>
<span class="hljs-built_in">console</span>.log(name.toUpperCase());  <span class="hljs-comment">// Output: JOHN</span>
</code></pre>
<p>Here, the variable <code>name</code> is re-assigned to a new string value, <code>'John'</code>. Since the type is still <code>string</code>, you can use string methods like <code>.toUpperCase()</code> without any issues.</p>
<p>You can also define arrays with type annotations. This ensures that the array only contains elements of a specific type. For example, if you define an array of numbers, TypeScript will allow you to use array methods that are specific to numbers.</p>
<p><code>test.ts</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> numbers: number[] = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];  <span class="hljs-comment">// Type is explicitly set as an array of numbers</span>
numbers.push(<span class="hljs-number">4</span>);  <span class="hljs-comment">// This is fine, as 4 is a number</span>

<span class="hljs-comment">// Accessing array method</span>
<span class="hljs-built_in">console</span>.log(numbers.length);  <span class="hljs-comment">// Output: 4</span>
</code></pre>
<p>In this case, <code>numbers</code> is an array of numbers. You can safely use array methods like <code>.push()</code> and <code>.length</code>, which are valid for number arrays.</p>
<p>If you try to reassign a variable to a value of an incompatible type, TypeScript will catch the error immediately during development, before the code is even run.</p>
<p>For instance:</p>
<p><code>test.ts</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> name: string = <span class="hljs-string">'Stephen'</span>;
name = <span class="hljs-number">2</span>;  <span class="hljs-comment">// Error: Type '2' is not assignable to type 'string'</span>
</code></pre>
<p>Here, you're trying to assign a number (<code>2</code>) to a variable that was previously declared as a string. TypeScript throws an error immediately, indicating that a number cannot be assigned to a string variable.</p>
<p>Similarly, for an array:</p>
<p><code>test.ts</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> numbers: number[] = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
numbers = <span class="hljs-string">'Hello'</span>;  <span class="hljs-comment">// Error: Type 'string' is not assignable to type 'number[]'</span>
</code></pre>
<p>Here, you're trying to assign a string (<code>'Hello'</code>) to a variable that was previously declared as an array of numbers. TypeScript catches this error and highlights the mismatch.</p>
<p>Experiment with different types to see how TypeScript enforces type safety. For example, try using boolean, number, or other types in your arrays and variables.</p>
<p>Now that you've seen how type annotations work with strings and arrays, it's time to experiment with other types. TypeScript allows you to define arrays and variables with various types, ensuring type safety across your code. Try creating arrays with other data types such as <code>boolean</code>, <code>number</code>.</p>
<h4 id="heading-example-boolean-array">Example: Boolean Array</h4>
<p><code>test.ts</code></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> booleanArray: <span class="hljs-built_in">Array</span>&lt;<span class="hljs-built_in">boolean</span>&gt; = [<span class="hljs-literal">true</span>, <span class="hljs-literal">false</span>, <span class="hljs-literal">true</span>];

<span class="hljs-comment">// Accessing array method</span>
<span class="hljs-built_in">console</span>.log(booleanArray.length);  <span class="hljs-comment">// Output: 3</span>
</code></pre>
<p>In this example, the array <code>booleanArray</code> is explicitly declared to hold only <code>boolean</code> values. Try adding <code>string</code> or <code>number</code> elements to see how TypeScript catches type errors.</p>
<h4 id="heading-example-number-array">Example: Number Array</h4>
<p><code>test.ts</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> numberArray: <span class="hljs-built_in">Array</span>&lt;number&gt; = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];

<span class="hljs-comment">// Accessing array method</span>
<span class="hljs-built_in">console</span>.log(numberArray[<span class="hljs-number">0</span>] * <span class="hljs-number">2</span>);  <span class="hljs-comment">// Output: 2</span>
</code></pre>
<p>Feel free to play around with these examples and observe how TypeScript provides strong type safety and catches errors in real time. The more you explore, the better you'll understand how to leverage TypeScript's type system to write cleaner and more reliable code.</p>
<h3 id="heading-what-is-type-inference">What is Type Inference?</h3>
<p>Type inference in TypeScript is a powerful feature that allows the TypeScript compiler to automatically determine the type of a variable based on the value assigned to it. TypeScript is designed to be smart enough to infer types in many cases, reducing the need for explicit type annotations. This enhances code conciseness while maintaining the benefits of type safety.</p>
<p>With type inference, TypeScript can predict the type of a variable by analyzing the value assigned to it, ensuring that you don’t need to specify the type manually, yet still receive all the advantages of type checking.</p>
<h5 id="heading-example-1-inferred-string-type"><strong>Example 1</strong>: Inferred String Type</h5>
<p><code>test.ts</code></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> message = <span class="hljs-string">"Hello, TypeScript!"</span>;  <span class="hljs-comment">// TypeScript infers 'message' as a string</span>
<span class="hljs-built_in">console</span>.log(message.toUpperCase());  <span class="hljs-comment">// Output: HELLO, TYPESCRIPT!</span>
</code></pre>
<p>In this example, TypeScript automatically infers the type of <code>message</code> as a <code>string</code> because the assigned value is a string.</p>
<h5 id="heading-example-2-inferred-number-type"><strong>Example 2</strong>: Inferred Number Type</h5>
<p><code>test.ts</code></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> count = <span class="hljs-number">42</span>;  <span class="hljs-comment">// TypeScript infers 'count' as a number</span>
<span class="hljs-built_in">console</span>.log(count + <span class="hljs-number">8</span>);  <span class="hljs-comment">// Output: 50</span>
</code></pre>
<p>Here, TypeScript infers the type of <code>count</code> as a <code>number</code> based on the value <code>42</code>, and you can perform arithmetic operations on it without type errors.</p>
<h5 id="heading-example-3-inferred-array-type"><strong>Example 3:</strong> Inferred Array Type</h5>
<p><code>test.ts</code></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> numbers = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];  <span class="hljs-comment">// TypeScript infers 'numbers' as an array of numbers (number[])</span>
<span class="hljs-built_in">console</span>.log(numbers.length);  <span class="hljs-comment">// Output: 3</span>
</code></pre>
<p>In this case, TypeScript infers that <code>numbers</code> is an array of type <code>number[]</code> because the array contains numbers.</p>
<h4 id="heading-incorrect-examples"><strong>Incorrect Examples:</strong></h4>
<h5 id="heading-example-4-mismatched-type-assignment"><strong>Example 4</strong>: Mismatched Type Assignment</h5>
<p><code>test.ts</code></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> count = <span class="hljs-number">42</span>;  <span class="hljs-comment">// TypeScript infers 'count' as a number</span>
count = <span class="hljs-string">"Not a number"</span>;  <span class="hljs-comment">// Error: Type 'string' is not assignable to type 'number'</span>
</code></pre>
<p>Even though TypeScript inferred that <code>count</code> is a number, attempting to assign a <code>string</code> to it results in an error. TypeScript catches this as a type mismatch because <code>count</code> was initially inferred as a <code>number</code>.</p>
<h5 id="heading-example-5-inferred-array-type-with-mixed-types"><strong>Example 5:</strong> Inferred Array Type with Mixed Types</h5>
<p><code>test.ts</code></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> mixedArray = [<span class="hljs-number">1</span>, <span class="hljs-string">"apple"</span>, <span class="hljs-literal">true</span>];  <span class="hljs-comment">// TypeScript infers 'mixedArray' as (string | number | boolean)[]</span>
<span class="hljs-built_in">console</span>.log(mixedArray[<span class="hljs-number">0</span>].toFixed(<span class="hljs-number">2</span>));  <span class="hljs-comment">// Error: Property 'toFixed' does not exist on type 'string | boolean'.</span>
</code></pre>
<p>In this example, TypeScript infers <code>mixedArray</code> as an array containing multiple types (<code>string | number | boolean</code>). While this is allowed, accessing methods like <code>.toFixed()</code> on elements may result in errors because not all array elements support that method (for example, <code>boolean</code> and <code>string</code> do not have <code>.toFixed()</code>).</p>
<h5 id="heading-example-6-inferred-type-with-incorrect-operation"><strong>Example 6</strong>: Inferred Type with Incorrect Operation</h5>
<p><code>test.ts</code></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> price = <span class="hljs-number">99.99</span>;  <span class="hljs-comment">// TypeScript infers 'price' as a number</span>
price = <span class="hljs-string">"Free"</span>;  <span class="hljs-comment">// Error: Type 'string' is not assignable to type 'number'</span>
</code></pre>
<p>Here, TypeScript infers that <code>price</code> is a <code>number</code>, but trying to reassign it to a <code>string</code> leads to a type error, ensuring that the variable maintains its inferred type.</p>
<h2 id="heading-the-union-and-any-types">The Union and Any Types</h2>
<p>In earlier examples, we used mixed types. Now, let’s properly define these concepts and expand on them with various examples:</p>
<h3 id="heading-what-are-union-types"><strong>What are Union Types?</strong></h3>
<p>Union types allow variables or parameters to hold multiple specific types, offering flexibility while maintaining type safety. You define a union type using the pipe (<code>|</code>) symbol.</p>
<p><strong>Simple Union Type:</strong></p>
<p><code>test.ts</code></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> value: <span class="hljs-built_in">string</span> | <span class="hljs-built_in">number</span>;

value = <span class="hljs-string">"Hello"</span>;  <span class="hljs-comment">// ✅ Correct</span>
<span class="hljs-built_in">console</span>.log(value.toUpperCase());  <span class="hljs-comment">// Output: HELLO</span>

value = <span class="hljs-number">42</span>;  <span class="hljs-comment">// ✅ Correct</span>
<span class="hljs-built_in">console</span>.log(value + <span class="hljs-number">8</span>);  <span class="hljs-comment">// Output: 50</span>

value = <span class="hljs-literal">true</span>;  <span class="hljs-comment">// ❌ Error: Type 'boolean' is not assignable to type 'string | number'.</span>
</code></pre>
<p>In this example, <code>value</code> can either be a string or a number. Any other type of assignment results in a type error.</p>
<p><strong>Union Type in Function Parameters:</strong>  </p>
<p><code>test.ts</code></p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">printId</span>(<span class="hljs-params">id: <span class="hljs-built_in">string</span> | <span class="hljs-built_in">number</span></span>): <span class="hljs-title">void</span> </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Your ID is: <span class="hljs-subst">${id}</span>`</span>);
}

printId(<span class="hljs-number">12345</span>);      <span class="hljs-comment">// ✅ Correct</span>
printId(<span class="hljs-string">"abc123"</span>);   <span class="hljs-comment">// ✅ Correct</span>
printId(<span class="hljs-literal">true</span>);       <span class="hljs-comment">// ❌ Error: Type 'boolean' is not assignable to type 'string | number'.</span>
</code></pre>
<p>Here, the <code>id</code> <strong>the</strong> parameter can only accept a <code>string</code> or <code>number</code>, ensuring type safety while providing flexibility.</p>
<p><strong>Custom Union Type:</strong></p>
<p>You can create custom types using the <code>type</code> keyword for better readability and reusability.  </p>
<p><code>test.ts</code></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> ID = <span class="hljs-built_in">string</span> | <span class="hljs-built_in">number</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getUser</span>(<span class="hljs-params">id: ID</span>): <span class="hljs-title">void</span> </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Fetching user with ID: <span class="hljs-subst">${id}</span>`</span>);
}

getUser(<span class="hljs-number">12345</span>);      <span class="hljs-comment">// ✅ Correct</span>
getUser(<span class="hljs-string">"abc123"</span>);   <span class="hljs-comment">// ✅ Correct</span>
getUser(<span class="hljs-literal">true</span>);       <span class="hljs-comment">// ❌ Error: Type 'boolean' is not assignable to type 'string | number'.</span>
</code></pre>
<h3 id="heading-what-is-the-any-type"><strong>What is the</strong> <code>any</code> Type?</h3>
<p>The <code>any</code> type is the most flexible type in TypeScript. It allows a variable to hold any type of value, disabling type-checking for that variable.</p>
<p>The <code>any</code> type sacrifices type safety for maximum flexibility. This is useful in scenarios where you are unsure about the type or you’re working with dynamic data.</p>
<h5 id="heading-example-1-array-of-any-type"><strong>Example 1</strong>: Array of any Type</h5>
<p><code>test.ts</code></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> mixedArray: <span class="hljs-built_in">any</span>[] = [<span class="hljs-number">1</span>, <span class="hljs-string">"apple"</span>, <span class="hljs-literal">true</span>];

<span class="hljs-built_in">console</span>.log(mixedArray[<span class="hljs-number">0</span>]);  <span class="hljs-comment">// Output: 1</span>
<span class="hljs-built_in">console</span>.log(mixedArray[<span class="hljs-number">1</span>].toUpperCase());  <span class="hljs-comment">// Output: APPLE</span>
<span class="hljs-built_in">console</span>.log(mixedArray[<span class="hljs-number">2</span>]);  <span class="hljs-comment">// Output: true</span>
</code></pre>
<p>Here, the <code>mixedArray</code> can hold elements of any type without triggering type errors.</p>
<h4 id="heading-when-to-use-union-vs-any"><strong>When to Use Union vs.</strong> <code>any</code></h4>
<ul>
<li><p><strong>Union Types</strong>: Use union types when the possible values are known or constrained to a few specific types. It provides type safety and avoids runtime errors.</p>
</li>
<li><p><code>any</code> <strong>Type</strong>: Use <code>any</code> as a last resort when the type is unknown or dynamic.</p>
</li>
</ul>
<p>Just remember that overusing <code>any</code> can negate the benefits of TypeScript’s type system. By carefully choosing between union types and <code>any</code>, you can write TypeScript code that is both flexible and type-safe.</p>
<h3 id="heading-be-careful-when-using-any-in-typescript"><strong>Be Careful When Using</strong> <code>any</code> in TypeScript</h3>
<p>The <code>any</code> type in TypeScript is a powerful yet risky feature. While this flexibility can sometimes be useful, it often leads to unintended behaviors or errors that TypeScript cannot catch at compile time.</p>
<p>Let’s explore an example to understand the potential pitfalls.</p>
<p>Here’s a function that demonstrates the risks:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">combineValues</span>(<span class="hljs-params">value: <span class="hljs-built_in">any</span></span>) </span>{
  <span class="hljs-keyword">let</span> anotherValue: <span class="hljs-built_in">number</span> = <span class="hljs-number">10</span>;

  <span class="hljs-keyword">return</span> value + anotherValue;
}

<span class="hljs-keyword">const</span> result = combineValues(<span class="hljs-number">5</span>); <span class="hljs-comment">// No error here.</span>
<span class="hljs-keyword">const</span> anotherResult = result;

<span class="hljs-comment">// Attempting to call a method on `anotherResult`</span>
anotherResult.someUndefinedMethod(); <span class="hljs-comment">// No compile-time error!</span>
</code></pre>
<p>What happened here?</p>
<p>First, we didn’t have any type checking with <code>any</code>. The parameter <code>value</code> is of type <code>any</code>, meaning it can hold any value: a string, number, object, and so on. TypeScript skips enforcing type checks on <code>value</code>.</p>
<p>Second, the return value assumes <code>any</code>. Since <code>value</code> is <code>any</code>, the return type of <code>combineValues</code> is also inferred as <code>any</code>.</p>
<p>Third, there’s no error when calling an undefined method. After the function is called, <code>anotherResult</code> is also treated as <code>any</code>. TypeScript allows calling any method (even non-existent ones) on a variable of type <code>any</code> without throwing errors. In this case, <code>someUndefinedMethod</code> doesn’t exist, but TypeScript won’t warn you.</p>
<h4 id="heading-the-risks-of-using-any"><strong>The Risks of Using</strong> <code>any</code></h4>
<ol>
<li><p><strong>Loss of type safety</strong>: You lose the benefits of TypeScript’s type system, like compile-time error checking. Potential runtime errors can go unnoticed during development.</p>
</li>
<li><p><strong>Accidental behavior</strong>: The function could accept unexpected inputs (e.g., strings, arrays, or objects), leading to incorrect results or crashes.</p>
</li>
<li><p><strong>Debugging complexity</strong>: Since the type is not enforced, debugging issues caused by incorrect types becomes more challenging.</p>
</li>
</ol>
<h3 id="heading-how-to-fix-this"><strong>How to Fix This</strong></h3>
<h4 id="heading-use-explicit-types-for-parameters-and-return-values"><strong>Use Explicit Types for Parameters and Return Values</strong></h4>
<p>Here’s an improved version with proper type annotations:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">combineValues</span>(<span class="hljs-params">value: <span class="hljs-built_in">number</span></span>): <span class="hljs-title">number</span> </span>{
  <span class="hljs-keyword">let</span> anotherValue: <span class="hljs-built_in">number</span> = <span class="hljs-number">10</span>;

  <span class="hljs-keyword">return</span> value + anotherValue;
}

<span class="hljs-keyword">const</span> result = combineValues(<span class="hljs-number">5</span>);
<span class="hljs-comment">// result.someUndefinedMethod(); // Error: Property 'someUndefinedMethod' does not exist on type 'number'.</span>
</code></pre>
<ol>
<li><p><strong>Parameter type</strong>: The function now explicitly expects a <code>number</code> for the <code>value</code> parameter.</p>
</li>
<li><p><strong>Return type</strong>: The return type is declared as <code>number</code>, ensuring that only numbers are returned.</p>
</li>
</ol>
<p>This ensures that TypeScript will throw errors if you try to pass invalid types or call methods that don’t exist on the return value.</p>
<h4 id="heading-key-takeaways"><strong>Key Takeaways</strong></h4>
<ul>
<li><p>The <code>any</code> type disables TypeScript’s type checking, making your code vulnerable to runtime errors.</p>
</li>
<li><p>Avoid using <code>any</code> whenever possible. Instead, use explicit types or stricter alternatives like <code>unknown</code> (if the type cannot be determined upfront).</p>
</li>
<li><p>Explicit types enhance code clarity, maintainability, and reliability by leveraging TypeScript’s compile-time checks.</p>
</li>
</ul>
<p>If you’re tempted to use <code>any</code> because the type isn’t clear, consider refactoring your code or using <code>unknown</code> combined with type guards for better safety.</p>
<h3 id="heading-using-unknown-as-a-safer-alternative-to-any-in-typescript"><strong>Using</strong> <code>unknown</code> as a Safer Alternative to <code>any</code> in TypeScript</h3>
<p>The <code>unknown</code> type in TypeScript is a stricter and safer alternative to <code>any</code>. While both <code>any</code> and <code>unknown</code> can hold values of any type, <code>unknown</code> requires you to perform type checks before using the value. This ensures greater type safety while still offering flexibility.</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">processValue</span>(<span class="hljs-params">input: unknown</span>): <span class="hljs-title">string</span> </span>{
  <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> input === <span class="hljs-string">'string'</span>) {
    <span class="hljs-keyword">return</span> <span class="hljs-string">`The value is a string: <span class="hljs-subst">${input}</span>`</span>;
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> input === <span class="hljs-string">'number'</span>) {
    <span class="hljs-keyword">return</span> <span class="hljs-string">`The value is a number: <span class="hljs-subst">${input}</span>`</span>;
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-string">'The value is of an unknown type'</span>;
  }
}

<span class="hljs-built_in">console</span>.log(processValue(<span class="hljs-string">'Hello, TypeScript!'</span>)); <span class="hljs-comment">// The value is a string: Hello, TypeScript!</span>
<span class="hljs-built_in">console</span>.log(processValue(<span class="hljs-number">42</span>)); <span class="hljs-comment">// The value is a number: 42</span>
<span class="hljs-built_in">console</span>.log(processValue(<span class="hljs-literal">true</span>)); <span class="hljs-comment">// The value is of an unknown type</span>
</code></pre>
<p>Using <code>unknown</code> instead of any has a few benefits:</p>
<ol>
<li><p><strong>Type-safe handling</strong>: Unlike <code>any</code>, <code>unknown</code> forces you to check the type of the value before using it. This prevents runtime errors caused by invalid operations on unexpected types.</p>
</li>
<li><p><strong>Explicit type narrowing</strong>: TypeScript requires you to narrow <code>unknown</code> to a specific type (e.g., <code>string</code>, <code>number</code>) using type guards (<code>typeof</code>, <code>instanceof</code>, etc.) before you can access its properties or methods.</p>
</li>
<li><p><strong>Enhanced code clarity</strong>: By using <code>unknown</code>, you signal to other developers that the type is deliberately uncertain and must be checked before use.</p>
</li>
</ol>
<h3 id="heading-key-differences-any-vs-unknown"><strong>Key Differences:</strong> <code>any</code> vs. <code>unknown</code></h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Feature</strong></td><td><code>any</code></td><td><code>unknown</code></td></tr>
</thead>
<tbody>
<tr>
<td>Type checking</td><td>No type checking</td><td>Requires type checks before usage</td></tr>
<tr>
<td>Flexibility</td><td>Can be used directly</td><td>Must narrow the type first</td></tr>
<tr>
<td>Common use case</td><td>Quick fixes (discouraged)</td><td>Safely handling uncertain types</td></tr>
</tbody>
</table>
</div><p>So to summarize, use <code>unknown</code> over <code>any</code> whenever you deal with values of uncertain types. It helps maintain type safety and reduces the risk of errors. And try to avoid <code>any</code> unless necessary, as it bypasses TypeScript’s safety features.</p>
<h2 id="heading-objects-in-typescript">Objects in TypeScript</h2>
<p>In TypeScript, objects are collections of properties where each property has a name (key) and a value. TypeScript allows us to define types for these properties, ensuring that objects conform to a specific structure.</p>
<p><code>test.ts</code></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> car = { car: <span class="hljs-string">'Toyota'</span>, brand: <span class="hljs-number">2024</span> };
<span class="hljs-built_in">console</span>.log(car);
</code></pre>
<p>This works fine because TypeScript infers the types for <code>car</code> and <code>brand</code> automatically based on the values provided.</p>
<h3 id="heading-explicit-object-types"><strong>Explicit Object Types</strong></h3>
<p>When we want to define the shape of an object explicitly, we can use inline type annotations. This makes it clear what type each property should have. For example:</p>
<p><code>test.ts</code></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> carOne: { car: <span class="hljs-built_in">string</span>; brand: <span class="hljs-built_in">number</span> } = { car: <span class="hljs-string">'Evil Spirit'</span>, brand: <span class="hljs-number">2025</span> };
<span class="hljs-built_in">console</span>.log(carOne);
</code></pre>
<p>This ensures that <code>carOne</code> always has a <code>car</code> property of type <code>string</code> and a <code>brand</code> property of type <code>number</code>.</p>
<p>Let’s say we want to add a <code>color</code> property to <code>carOne</code>:</p>
<p><code>test.ts</code></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> carOne: { car: <span class="hljs-built_in">string</span>; brand: <span class="hljs-built_in">number</span> } = { car: <span class="hljs-string">'Evil Spirit'</span>, brand: <span class="hljs-number">2025</span>, color: <span class="hljs-string">'Black'</span> };
</code></pre>
<p>The code above will show a redline because <code>color</code> is not part of the defined type <code>{ car: string; brand: number }</code>. The error will look something like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736933755272/8a3d48dd-3ae0-4769-9e13-fa1f6ca37331.png" alt="8a3d48dd-3ae0-4769-9e13-fa1f6ca37331" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<blockquote>
<p>Type '{ car: string; brand: number; color: string; }' is not assignable to type '{ car: string; brand: number; }'. Object literal may only specify known properties, and 'color' does not exist in type '{ car: string; brand: number; }'.</p>
</blockquote>
<p>Similarly, if you try to change the type of <code>brand</code> to a <code>string</code>:</p>
<p><code>test.ts</code></p>
<pre><code class="lang-typescript">carOne.brand = <span class="hljs-string">"2026"</span>;
</code></pre>
<p>You’ll get another error:</p>
<blockquote>
<p>Type 'string' is not assignable to type 'number'.</p>
</blockquote>
<p>Having to write the full object type each time can get repetitive, especially for objects with many properties or when the same structure is used in multiple places. But don’t worry – I’ll soon introduce <strong>type aliases</strong>, which make defining and reusing object types much simpler. You’ll see how to use type aliases to simplify object types and make your code cleaner. After that, we’ll explore how to apply these concepts in React.</p>
<p>For now, focus on understanding the basics and how TypeScript enforces structure. It’s like peeking under the hood to see how TypeScript works behind the scenes.</p>
<h3 id="heading-objects-and-arrays"><strong>Objects and Arrays</strong></h3>
<p>In TypeScript, we often deal with arrays of objects, where each object has a specific structure. TypeScript helps ensure that every object in the array conforms to the expected type.</p>
<p>Imagine you are managing a grocery store, and you want to keep track of your vegetables. Here’s how you might start:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> tomato = { name: <span class="hljs-string">'Tomato'</span>, price: <span class="hljs-number">2</span> };
<span class="hljs-keyword">let</span> potato = { name: <span class="hljs-string">'Potato'</span>, price: <span class="hljs-number">1</span> };
<span class="hljs-keyword">let</span> carrot = { name: <span class="hljs-string">'Carrot'</span> };

<span class="hljs-keyword">let</span> vegetables: { name: <span class="hljs-built_in">string</span>; price: <span class="hljs-built_in">number</span> }[] = [tomato, potato, carrot];
</code></pre>
<p>When TypeScript checks this code, it throws an error because <code>carrot</code> doesn’t have a <code>price</code> property. The expected type for each item in the <code>vegetables</code> array is <code>{ name: string; price: number }</code>. Since <code>carrot</code> is missing the <code>price</code>, TypeScript flags it as an error.</p>
<blockquote>
<p>Type '{ name: string; }' is not assignable to type '{ name: string; price: number; }'. Property 'price' is missing in type '{ name: string; }' but required in type '{ name: string; price: number; }'.</p>
</blockquote>
<p>If the <code>price</code> is not always known or applicable (for example, maybe the carrot's price is still being negotiated), you can make the <code>price</code> property optional. You can do this by adding a <code>?</code> after the property name:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> vegetables: { name: <span class="hljs-built_in">string</span>; price?: <span class="hljs-built_in">number</span> }[] = [tomato, potato, carrot];
</code></pre>
<p>Now, TypeScript knows that the <code>price</code> property is optional. This means objects in the <code>vegetables</code> array can either include <code>price</code> or omit it without causing errors.</p>
<p>When a property is optional, TypeScript allows it to be either:</p>
<ol>
<li><p>Present with the specified type.</p>
</li>
<li><p>Absent altogether.</p>
</li>
</ol>
<p>This flexibility eliminates the error for objects like <code>carrot</code>, which lack the <code>price</code> property.</p>
<h3 id="heading-the-readonly-modifier"><strong>The</strong> <code>readonly</code> Modifier</h3>
<p>In TypeScript, the <code>readonly</code> modifier is a great way to ensure that certain properties or entire objects remain immutable. This is particularly useful when you want to prevent accidental changes to your data.</p>
<p>Let’s continue with our vegetable store example and see how <code>readonly</code> works.</p>
<h4 id="heading-the-problem-of-mutability"><strong>The Problem of Mutability</strong></h4>
<p>Imagine we have this setup:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> tomato = { name: <span class="hljs-string">'Tomato'</span>, price: <span class="hljs-number">2</span> };
<span class="hljs-keyword">let</span> potato = { name: <span class="hljs-string">'Potato'</span>, price: <span class="hljs-number">1</span> };
<span class="hljs-keyword">let</span> carrot = { name: <span class="hljs-string">'Carrot'</span> };

<span class="hljs-keyword">let</span> vegetables: { name: <span class="hljs-built_in">string</span>; price?: <span class="hljs-built_in">number</span> }[] = [tomato, potato, carrot];
</code></pre>
<p>If someone accidentally tries to change the <code>name</code> of the <code>tomato</code> object or remove the <code>carrot</code> object from the <code>vegetables</code> array, TypeScript won’t complain:</p>
<pre><code class="lang-typescript">vegetables[<span class="hljs-number">0</span>].name = <span class="hljs-string">'Cucumber'</span>; <span class="hljs-comment">// No error, but this could be unintended!</span>
vegetables.pop(); <span class="hljs-comment">// Removes the last vegetable, no warning.</span>
</code></pre>
<p>We can use <code>readonly</code> to make these objects and arrays immutable, ensuring their original state cannot be altered.</p>
<h3 id="heading-readonly-on-object-properties"><strong>Readonly on Object Properties</strong></h3>
<p>To make the properties of each vegetable immutable, you can do the following:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> vegetables: { <span class="hljs-keyword">readonly</span> name: <span class="hljs-built_in">string</span>; <span class="hljs-keyword">readonly</span> price?: <span class="hljs-built_in">number</span> }[] = [
  { name: <span class="hljs-string">'Tomato'</span>, price: <span class="hljs-number">2</span> },
  { name: <span class="hljs-string">'Potato'</span>, price: <span class="hljs-number">1</span> },
  { name: <span class="hljs-string">'Carrot'</span> },
];
</code></pre>
<p>Now, if you try to change the <code>name</code> or <code>price</code> of any vegetable, TypeScript throws an error:</p>
<pre><code class="lang-typescript">typescriptCopy codevegetables[<span class="hljs-number">0</span>].name = <span class="hljs-string">'Cucumber'</span>; <span class="hljs-comment">// Error: Cannot assign to 'name' because it is a read-only</span>
</code></pre>
<h3 id="heading-readonly-arrays"><strong>Readonly Arrays</strong></h3>
<p>You can also make the entire <code>vegetables</code> array immutable by declaring it as <code>readonly</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> vegetables: <span class="hljs-keyword">readonly</span> { name: <span class="hljs-built_in">string</span>; price?: <span class="hljs-built_in">number</span> }[] = [
  { name: <span class="hljs-string">'Tomato'</span>, price: <span class="hljs-number">2</span> },
  { name: <span class="hljs-string">'Potato'</span>, price: <span class="hljs-number">1</span> },
  { name: <span class="hljs-string">'Carrot'</span> },
];
</code></pre>
<p>This prevents operations that modify the array itself, such as <code>push</code>, <code>pop</code>, or <code>splice</code>:</p>
<pre><code class="lang-typescript">vegetables.push({ name: <span class="hljs-string">'Onion'</span>, price: <span class="hljs-number">3</span> }); <span class="hljs-comment">// Error: Property 'push' does not exist on type 'readonly { name: string; price?: number; }[]'.</span>
vegetables.pop(); <span class="hljs-comment">// Error: Property 'pop' does not exist on type 'readonly { name: string; price?: number; }[]'.</span>
</code></pre>
<h3 id="heading-when-to-use-readonly"><strong>When to Use</strong> <code>readonly</code></h3>
<ol>
<li><p><strong>Immutable data</strong>: Use <code>readonly</code> when you want to enforce immutability for objects or arrays, especially in contexts where data should remain constant (e.g., configurations, initial states, constants).</p>
</li>
<li><p><strong>Prevent bugs</strong>: Protect your data from accidental changes caused by other parts of the code.</p>
</li>
</ol>
<h3 id="heading-complete-example"><strong>Complete Example</strong></h3>
<p>Here’s an updated example with <code>readonly</code> in action:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> vegetables: <span class="hljs-keyword">readonly</span> { <span class="hljs-keyword">readonly</span> name: <span class="hljs-built_in">string</span>; <span class="hljs-keyword">readonly</span> price?: <span class="hljs-built_in">number</span> }[] = [
  { name: <span class="hljs-string">'Tomato'</span>, price: <span class="hljs-number">2</span> },
  { name: <span class="hljs-string">'Potato'</span>, price: <span class="hljs-number">1</span> },
  { name: <span class="hljs-string">'Carrot'</span> },
];

<span class="hljs-comment">// Attempting to modify data</span>
vegetables[<span class="hljs-number">0</span>].name = <span class="hljs-string">'Cucumber'</span>; <span class="hljs-comment">// Error: Cannot assign to 'name' because it is a read-only property.</span>
vegetables.pop(); <span class="hljs-comment">// Error: Property 'pop' does not exist on type 'readonly { readonly name: string; readonly price?: number; }[]'.</span>

<span class="hljs-built_in">console</span>.log(vegetables);
</code></pre>
<p>Here’s what you should know about readonly, summarized:</p>
<ul>
<li><p><code>readonly</code> on properties ensures individual fields of objects cannot be changed.</p>
</li>
<li><p><code>readonly</code> on arrays makes the array itself immutable, preventing operations like <code>push</code> and <code>pop</code>.</p>
</li>
<li><p>Combining both provides full immutability for objects within an array.</p>
</li>
</ul>
<p>By using <code>readonly</code>, you create safer, more predictable code, reducing bugs caused by unintended mutations.</p>
<h2 id="heading-function-params-and-function-returns">Function Params and Function Returns</h2>
<p>Functions in TypeScript allow you to define both the <strong>parameters</strong> and the <strong>return types</strong> explicitly. This ensures that the function behaves as expected and avoids runtime errors. Let's break this down with a simple example.</p>
<h3 id="heading-inferred-return-type"><strong>Inferred Return Type</strong></h3>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">arithmeticOp</span>(<span class="hljs-params">price: <span class="hljs-built_in">number</span></span>) </span>{
  <span class="hljs-keyword">return</span> price * <span class="hljs-number">9</span>;
}

<span class="hljs-keyword">const</span> FP = arithmeticOp(<span class="hljs-number">2</span>); <span class="hljs-comment">// The result is 18.</span>
</code></pre>
<ol>
<li><p>The parameter <code>price</code> is explicitly defined as a <code>number</code>.</p>
</li>
<li><p>The return type is not explicitly stated, but TypeScript <strong>infers</strong> it to be a <code>number</code> because the function returns <code>price * 9</code>, which is a numeric operation.</p>
</li>
</ol>
<p>TypeScript is smart enough to infer the return type of the function based on the return statement. In this case, it correctly infers that <code>arithmeticOp</code> returns a <code>number</code>.</p>
<h3 id="heading-explicit-return-type"><strong>Explicit Return Type</strong></h3>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">arithmeticOp</span>(<span class="hljs-params">price: <span class="hljs-built_in">number</span></span>): <span class="hljs-title">number</span> </span>{
  <span class="hljs-keyword">return</span> price * <span class="hljs-number">9</span>;
}

<span class="hljs-keyword">const</span> FP = arithmeticOp(<span class="hljs-number">2</span>); <span class="hljs-comment">// The result is still 18.</span>
</code></pre>
<ol>
<li><p>The function explicitly declares the return type as <code>number</code> using the syntax <code>functionName(parameters): returnType</code>.</p>
</li>
<li><p>This doesn’t change the result but makes the function declaration clearer.</p>
</li>
</ol>
<p>So why should you use explicit return types? Well, first of all it improves code readability and ensures that future changes don’t accidentally alter the return type. And second, it serves as documentation for other developers.</p>
<h3 id="heading-return-type-mismatch"><strong>Return Type Mismatch</strong></h3>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">arithmeticOp</span>(<span class="hljs-params">price: <span class="hljs-built_in">number</span></span>): <span class="hljs-title">number</span> </span>{
  <span class="hljs-keyword">if</span> (hasDiscount) {
    <span class="hljs-keyword">return</span> <span class="hljs-string">'discount'</span>; <span class="hljs-comment">// Error here!</span>
  }
  <span class="hljs-keyword">return</span> price * <span class="hljs-number">9</span>;
}

<span class="hljs-keyword">const</span> FP = arithmeticOp(<span class="hljs-number">2</span>);
</code></pre>
<p>In the code above, the return type is explicitly declared as <code>number</code>. But the function attempts to return a <code>string</code> (<code>'discount'</code>) in certain cases. This causes TypeScript to throw an error:</p>
<blockquote>
<p>Type 'string' is not assignable to type 'number'.</p>
</blockquote>
<p>This happens because TypeScript enforces the declared return type. If you say a function returns a <code>number</code>, it <strong>must always</strong> return a <code>number</code>, regardless of the logic inside the function.</p>
<p>If you want the function to return multiple types (for example, <code>number</code> or <code>string</code>), use a <strong>union type</strong>:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">arithmeticOp</span>(<span class="hljs-params">price: <span class="hljs-built_in">number</span></span>): <span class="hljs-title">number</span> | <span class="hljs-title">string</span> </span>{
  <span class="hljs-keyword">if</span> (hasDiscount) {
    <span class="hljs-keyword">return</span> <span class="hljs-string">'discount'</span>; <span class="hljs-comment">// Now valid!</span>
  }
  <span class="hljs-keyword">return</span> price * <span class="hljs-number">9</span>;
}

<span class="hljs-keyword">const</span> FP = arithmeticOp(<span class="hljs-number">2</span>);
</code></pre>
<p>The return type <code>number | string</code> tells TypeScript that the function can return either a <code>number</code> or a <code>string</code>. This resolves the type mismatch error.</p>
<h4 id="heading-key-takeaways-1">Key Takeaways:</h4>
<ol>
<li><p>TypeScript <strong>infers</strong> return types when they are not explicitly defined but encourages explicit return types for clarity and maintainability.</p>
</li>
<li><p>The declared return type ensures the function only returns values of the specified type.</p>
</li>
<li><p>Type mismatches, like returning a <code>string</code> from a function expected to return a <code>number</code>, result in TypeScript errors.</p>
</li>
<li><p>Union types (<code>type1 | type2</code>) allow functions to return multiple types when needed.</p>
</li>
</ol>
<h3 id="heading-handling-optional-default-in-typescript"><strong>Handling Optional, Default in TypeScript</strong></h3>
<p>When working with TypeScript functions, specifying parameter behavior is crucial for flexibility and preventing runtime errors. Let's explore how to handle optional and default parameters effectively with practical examples.</p>
<h3 id="heading-example-1-understanding-the-problem-with-missing-arguments">Example 1: Understanding the Problem with Missing Arguments</h3>
<p>Consider the following function:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">calculateFinalScore</span>(<span class="hljs-params">baseScore: <span class="hljs-built_in">number</span>, deductions: <span class="hljs-built_in">number</span></span>): <span class="hljs-title">number</span> </span>{
  <span class="hljs-keyword">return</span> baseScore - deductions;
}

<span class="hljs-keyword">let</span> scoreWithDeductions = calculateFinalScore(<span class="hljs-number">50</span>, <span class="hljs-number">10</span>);
<span class="hljs-keyword">let</span> scoreWithoutDeductions = calculateFinalScore(<span class="hljs-number">50</span>); <span class="hljs-comment">// Error</span>
</code></pre>
<p>The first call to <code>calculateFinalScore</code> works perfectly. But the second call throws a TypeScript error:</p>
<pre><code class="lang-typescript">⚠ <span class="hljs-built_in">Error</span> (TS2554) | Expected <span class="hljs-number">2</span> <span class="hljs-built_in">arguments</span>, but got <span class="hljs-number">1.</span>
Tutorial.ts(<span class="hljs-number">7</span>, <span class="hljs-number">47</span>): An argument <span class="hljs-keyword">for</span> <span class="hljs-string">'deductions'</span> was not provided.
</code></pre>
<p>This happens because TypeScript expects both <code>baseScore</code> and <code>deductions</code> to be provided, as they are both required parameters. If the <code>deductions</code> value is omitted, TypeScript will not allow the function call.</p>
<h3 id="heading-example-2-fixing-the-issue-with-default-parameters">Example 2: Fixing the Issue with Default Parameters</h3>
<p>To resolve this issue, we can define a default value for the <code>deductions</code> parameter. Default parameters provide a fallback value if no argument is passed.</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">calculateFinalScore</span>(<span class="hljs-params">baseScore: <span class="hljs-built_in">number</span>, deductions: <span class="hljs-built_in">number</span> = 0</span>): <span class="hljs-title">number</span> </span>{
  <span class="hljs-keyword">return</span> baseScore - deductions;
}

<span class="hljs-keyword">let</span> scoreWithDeductions = calculateFinalScore(<span class="hljs-number">50</span>, <span class="hljs-number">10</span>); <span class="hljs-comment">// 40</span>
<span class="hljs-keyword">let</span> scoreWithoutDeductions = calculateFinalScore(<span class="hljs-number">50</span>);  <span class="hljs-comment">// 50</span>
</code></pre>
<p>In this updated example:</p>
<ul>
<li><p>The <code>deductions</code> parameter defaults to <code>0</code> if it is not explicitly provided.</p>
</li>
<li><p>Both calls now work without errors.</p>
</li>
</ul>
<h3 id="heading-why-this-solution-works">Why This Solution Works</h3>
<p>By defining <code>deductions</code> as a default parameter, TypeScript ensures that the function has all the arguments it needs to execute, even if some are omitted in the call. This approach increases the flexibility of the function while maintaining type safety.</p>
<p>Use default parameters when a value is required for the function to work but can safely have a fallback value if omitted. This approach improves code clarity and reduces the likelihood of runtime errors.</p>
<h2 id="heading-rest-parameters">Rest Parameters</h2>
<p>Rest parameters in TypeScript let you handle multiple arguments without knowing how many you’ll get in advance. You can pass as many arguments as you want—TypeScript will handle them. They’re perfect for situations where the number of inputs isn’t fixed.</p>
<p>To use rest parameters, you write three dots (<code>...</code>) before the parameter name, which gathers all the extra arguments into an array.</p>
<p>Let’s say you want to combine multiple words into a single sentence:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">joinWords</span>(<span class="hljs-params">...words: <span class="hljs-built_in">string</span>[]</span>): <span class="hljs-title">string</span> </span>{
  <span class="hljs-keyword">return</span> words.join(<span class="hljs-string">" "</span>);
}

<span class="hljs-keyword">let</span> sentence = joinWords(<span class="hljs-string">"TypeScript"</span>, <span class="hljs-string">"makes"</span>, <span class="hljs-string">"coding"</span>, <span class="hljs-string">"fun"</span>);
<span class="hljs-built_in">console</span>.log(sentence); <span class="hljs-comment">// "TypeScript makes coding fun"</span>
</code></pre>
<ul>
<li><p><code>...words</code> collects all the arguments into an array (<code>["TypeScript", "makes", "coding", "fun"]</code>).</p>
</li>
<li><p>The <code>join</code> method combines them into a single string, separated by spaces.</p>
</li>
</ul>
<h3 id="heading-rest-parameters-with-numbers">Rest Parameters with Numbers</h3>
<p>Now, suppose you want to add multiple numbers:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sumNumbers</span>(<span class="hljs-params">...numbers: <span class="hljs-built_in">number</span>[]</span>): <span class="hljs-title">number</span> </span>{
  <span class="hljs-keyword">return</span> numbers.reduce(<span class="hljs-function">(<span class="hljs-params">total, num</span>) =&gt;</span> total + num, <span class="hljs-number">0</span>);
}

<span class="hljs-keyword">let</span> total = sumNumbers(<span class="hljs-number">10</span>, <span class="hljs-number">20</span>, <span class="hljs-number">30</span>);
<span class="hljs-built_in">console</span>.log(total); <span class="hljs-comment">// 60</span>
</code></pre>
<ul>
<li><p><code>...numbers</code> gathers all the numbers into an array (<code>[10, 20, 30]</code>).</p>
</li>
<li><p>The <code>reduce</code> method adds them together to get the total.</p>
</li>
</ul>
<p>We can also use rest parameters to merge multiple arrays into one:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mergeArrays</span>(<span class="hljs-params">...arrays: <span class="hljs-built_in">number</span>[][]</span>): <span class="hljs-title">number</span>[] </span>{
  <span class="hljs-keyword">return</span> arrays.flat();
}

<span class="hljs-keyword">let</span> combined = mergeArrays([<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-built_in">console</span>.log(combined); <span class="hljs-comment">// [1, 2, 3, 4, 5, 6]</span>
</code></pre>
<ul>
<li><p><code>...arrays</code> collects each argument as an array into an array of arrays (<code>[[1, 2], [3, 4], [5, 6]]</code>).</p>
</li>
<li><p>The <code>flat</code> method combines them into one array.</p>
</li>
</ul>
<p>Rest parameters must always come last in the parameter list. For example:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">example</span>(<span class="hljs-params">a: <span class="hljs-built_in">string</span>, ...others: <span class="hljs-built_in">number</span>[]</span>): <span class="hljs-title">void</span> </span>{
  <span class="hljs-built_in">console</span>.log(a, others);
}
</code></pre>
<p>This ensures all remaining arguments go into the rest parameter.</p>
<h2 id="heading-objects-as-parameters-in-typescript">Objects as Parameters in TypeScript</h2>
<p>In TypeScript, functions can accept objects as parameters. This is particularly useful when dealing with multiple related values.</p>
<h3 id="heading-using-objects-with-specific-properties">Using Objects with Specific Properties</h3>
<p>Here's a function that takes an object with an <code>id</code> property and returns a new object:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createEmployee</span>(<span class="hljs-params">{ id }: { id: <span class="hljs-built_in">number</span> }</span>): </span>{ id: <span class="hljs-built_in">number</span>; isActive: <span class="hljs-built_in">boolean</span> } {
  <span class="hljs-keyword">return</span> { id, isActive: id % <span class="hljs-number">2</span> === <span class="hljs-number">0</span> };
}

<span class="hljs-keyword">const</span> firstEmployee = createEmployee({ id: <span class="hljs-number">1</span> });
<span class="hljs-built_in">console</span>.log(firstEmployee); <span class="hljs-comment">// { id: 1, isActive: false }</span>

<span class="hljs-keyword">const</span> secondEmployee = createEmployee({ id: <span class="hljs-number">2</span> });
<span class="hljs-built_in">console</span>.log(secondEmployee); <span class="hljs-comment">// { id: 2, isActive: true }</span>
</code></pre>
<p>The function <code>createEmployee</code>:</p>
<ul>
<li><p>Takes an object with a single property, <code>id</code>, as a parameter.</p>
</li>
<li><p>Returns a new object with two properties: <code>id</code> and <code>isActive</code>.</p>
</li>
</ul>
<p>The <code>isActive</code> property is determined by checking if the <code>id</code> is even (<code>id % 2 === 0</code>).</p>
<p><strong>Destructuring</strong> is used in the parameter:</p>
<ul>
<li><code>{ id }</code> extracts the <code>id</code> property from the input object directly.</li>
</ul>
<h3 id="heading-accepting-more-complex-objects">Accepting More Complex Objects</h3>
<p>Now, let’s look at a function that takes an object with multiple properties:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createStudent</span>(<span class="hljs-params">student: { id: <span class="hljs-built_in">number</span>; name: <span class="hljs-built_in">string</span> }</span>): <span class="hljs-title">void</span> </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Welcome to the course, <span class="hljs-subst">${student.name}</span>!`</span>);
}

<span class="hljs-keyword">const</span> newStudent = { id: <span class="hljs-number">1</span>, name: <span class="hljs-string">"John"</span> };
createStudent(newStudent); <span class="hljs-comment">// "Welcome to the course, John!"</span>
</code></pre>
<p>The function <code>createStudent</code>:</p>
<ul>
<li><p>Accepts an object with two properties: <code>id</code> and <code>name</code>.</p>
</li>
<li><p>Logs a welcome message using the <code>name</code> property.</p>
</li>
</ul>
<p>The <code>newStudent</code> object matches the structure expected by the function, so it’s passed directly.</p>
<h3 id="heading-why-use-objects-as-parameters">Why Use Objects as Parameters?</h3>
<p>First of all, functions with objects as parameters are easier to read, especially when dealing with multiple related values. Also, using destructuring you can extract only the needed properties from an object, making the code more concise. And finally, objects can be reused across functions without creating new ones every time.</p>
<h3 id="heading-excess-property-checks-in-typescript">Excess Property Checks in TypeScript</h3>
<p>In TypeScript, excess property checks help ensure that objects passed to functions only contain properties defined in the function’s parameter type. If there are extra properties, TypeScript will raise an error. Let's see how this works with simple examples.</p>
<h4 id="heading-1-extra-property-error">1. Extra Property Error</h4>
<p>Here’s a function that accepts an object with <code>id</code> and <code>name</code>, but no extra properties:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createStudent</span>(<span class="hljs-params">student: { id: <span class="hljs-built_in">number</span>; name: <span class="hljs-built_in">string</span> }</span>): <span class="hljs-title">void</span> </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Welcome, <span class="hljs-subst">${student.name}</span>!`</span>);
}

<span class="hljs-keyword">const</span> newStudent = { id: <span class="hljs-number">1</span>, name: <span class="hljs-string">"John"</span>, age: <span class="hljs-number">20</span> }; <span class="hljs-comment">// Extra property 'age'</span>

createStudent(newStudent); <span class="hljs-comment">// Error: 'age' is not expected</span>
</code></pre>
<p>TypeScript gives an error because the <code>age</code> property is not part of the expected object structure.</p>
<h4 id="heading-2-fixing-the-error">2. Fixing the Error</h4>
<p>To avoid the error, just remove any extra properties:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> validStudent = { id: <span class="hljs-number">1</span>, name: <span class="hljs-string">"John"</span> };
createStudent(validStudent); <span class="hljs-comment">// This works fine</span>
</code></pre>
<p>This works because the object only has the expected properties: <code>id</code> and <code>name</code>.</p>
<h4 id="heading-3-using-type-assertion-not-recommended">3. Using Type Assertion (Not Recommended)</h4>
<p>If you really need to pass an object with extra properties, you can use <strong>type assertion</strong> to tell TypeScript to ignore the extra properties:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> studentWithExtras = { id: <span class="hljs-number">1</span>, name: <span class="hljs-string">"John"</span>, age: <span class="hljs-number">20</span> };
createStudent(studentWithExtras <span class="hljs-keyword">as</span> { id: <span class="hljs-built_in">number</span>; name: <span class="hljs-built_in">string</span> }); <span class="hljs-comment">// Bypasses the error</span>
</code></pre>
<p>While this works, it’s better to match the expected structure instead of using type assertion.</p>
<ul>
<li><p>TypeScript expects objects to match the exact shape of the parameter type.</p>
</li>
<li><p>Excess properties cause errors to ensure the structure is correct.</p>
</li>
<li><p>Fix the object or use type assertion (carefully) if you need extra properties.</p>
</li>
</ul>
<p>Excess property checks help keep your code safe and ensure only the right data is passed to functions.</p>
<h2 id="heading-type-aliases-in-typescript">Type Aliases in TypeScript</h2>
<p>A <strong>type alias</strong> in TypeScript is essentially a <strong>short name</strong> or an <strong>alternative name</strong> for an existing type. It allows you to define a simpler or more readable name for a type that may be complex or used repeatedly in your code.</p>
<p>This doesn't create a new type, but instead gives an existing type a new identifier. The functionality of the code doesn't change when using a type alias – it simply makes your code more readable and reusable.</p>
<p>Here’s an example before using a type alias:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Without type alias</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getUserInfo</span>(<span class="hljs-params">user: UserInfo</span>) </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`User Info: 
    Name: <span class="hljs-subst">${user.name}</span>, 
    Age: <span class="hljs-subst">${user.age}</span>, 
    Address: <span class="hljs-subst">${user.address}</span>`</span>);
}

<span class="hljs-keyword">const</span> user: UserInfo = { name: <span class="hljs-string">'Alice'</span>, age: <span class="hljs-number">30</span>, address: <span class="hljs-string">'123 Main St'</span> };

getUserInfo(user);
</code></pre>
<p>Now, let’s use a type alias for the function parameters to make the code more readable:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Using type alias</span>
<span class="hljs-keyword">type</span> UserInfo = { name: <span class="hljs-built_in">string</span>, age: <span class="hljs-built_in">number</span>, address: <span class="hljs-built_in">string</span> };

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getUserInfo</span>(<span class="hljs-params">user: UserInfo</span>) </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`User Info: 
    Name: <span class="hljs-subst">${user.name}</span>, 
    Age: <span class="hljs-subst">${user.age}</span>, 
    Address: <span class="hljs-subst">${user.address}</span>`</span>);
}

<span class="hljs-keyword">const</span> user: UserInfo = { name: <span class="hljs-string">'Alice'</span>, age: <span class="hljs-number">30</span>, address: <span class="hljs-string">'123 Main St'</span> };

getUserInfo(user);
</code></pre>
<p>In the example above:</p>
<ul>
<li><p>Before the type alias, we define the parameters separately within the function.</p>
</li>
<li><p>After defining a type alias (<code>UserInfo</code>), we use it in the function parameter to make the function signature simpler and more readable.</p>
</li>
</ul>
<p>This <strong>doesn’t change the functionality</strong> of the code. It just makes it easier to work with by using the alias. The alias acts as a reusable reference to a complex type, and if the shape of the <code>UserInfo</code> changes, we only need to update it in one place, making the code easier to maintain.</p>
<h3 id="heading-how-to-use-type-aliases">How to Use Type Aliases</h3>
<p>A type alias allows you to define a new name for a type. This new name can represent a primitive type, an object structure, or even a union of types. The main benefit is to make your code more readable, reusable, and prevent mistakes.</p>
<p>You define a type alias using the <code>type</code> keyword followed by a name and the structure of the type.</p>
<pre><code class="lang-typescript">ttype TypeName = TypeStructure;
</code></pre>
<p>For example, let’s create a type alias for a User object:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> User = {
  name: <span class="hljs-built_in">string</span>;
  age: <span class="hljs-built_in">number</span>;
</code></pre>
<p>This means <code>User</code> is a type that expects an object with two properties:</p>
<ul>
<li><p><code>name</code> should be a string.</p>
</li>
<li><p><code>age</code> should be a number.</p>
</li>
</ul>
<h3 id="heading-why-use-type-aliases">Why Use Type Aliases?</h3>
<p>There are several reasons to use type aliases in your code. First of all, a type alias explicitly defines the structure of an object, so anyone reading the code knows exactly what to expect. Second, you can reuse the <code>User</code> type anywhere in your code without repeating the structure. And finally, TypeScript will check that any object assigned to the <code>User</code> type has the required properties with the correct types.</p>
<h4 id="heading-with-type-alias">with Type Alias:</h4>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> User = {
  name: <span class="hljs-built_in">string</span>;
  age: <span class="hljs-built_in">number</span>;
};

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getUserDetails</span>(<span class="hljs-params">user: User</span>): <span class="hljs-title">string</span> </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-string">`<span class="hljs-subst">${user.name}</span> (<span class="hljs-subst">${user.age}</span> years old)`</span>;
}

<span class="hljs-keyword">const</span> user: User = { name: <span class="hljs-string">"Alice"</span>, age: <span class="hljs-number">30</span> };
<span class="hljs-built_in">console</span>.log(getUserDetails(user)); <span class="hljs-comment">// "Alice (30 years old)"</span>
</code></pre>
<p>In this example, we defined the <code>User</code> type alias to specify that <code>user</code> objects must have a <code>name</code> of type <code>string</code> and <code>age</code> of type <code>number</code>.</p>
<p>TypeScript will catch errors if you attempt to assign an object that does not match this structure, like this:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// This will result in a TypeScript error:</span>
<span class="hljs-keyword">const</span> invalidUser: User = { name: <span class="hljs-string">"Alice"</span> }; <span class="hljs-comment">// Missing 'age' property</span>
</code></pre>
<h3 id="heading-what-is-an-intersection-type-in-typescript">What is an <strong>Intersection Type</strong> in TypeScript?</h3>
<p>An <strong>Intersection Type</strong> is a powerful feature in TypeScript that allows you to combine multiple types into one. When you create an intersection, the resulting type must have <strong>all the properties</strong> from each of the types you intersect.</p>
<p>You can combine any number of types, and the resulting type must satisfy every condition of all the original types.</p>
<h4 id="heading-syntax-of-intersection-type">Syntax of Intersection Type</h4>
<p>To define an intersection type, you use the <code>&amp;</code> operator to combine two or more types.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> TypeA &amp; TypeB;
</code></pre>
<h4 id="heading-example-of-an-intersection-type">Example of an Intersection Type</h4>
<p>Imagine you want to extend the <code>User</code> type to include the user’s address. Instead of modifying the original <code>User</code> type, you can use an <strong>intersection type</strong> to combine <code>User</code> and <code>Address</code>.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> Address = {
  city: <span class="hljs-built_in">string</span>;
  country: <span class="hljs-built_in">string</span>;
};

<span class="hljs-keyword">type</span> UserWithAddress = User &amp; Address; <span class="hljs-comment">// Intersection of User and Address</span>
</code></pre>
<p>Now, <code>UserWithAddress</code> will require both the properties from <code>User</code> and the properties from <code>Address</code>.</p>
<h4 id="heading-example-with-a-function">Example with a Function</h4>
<p>Here’s how you can use this in a function:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> User = {
  name: <span class="hljs-built_in">string</span>;
  age: <span class="hljs-built_in">number</span>;
};

<span class="hljs-keyword">type</span> Address = {
  city: <span class="hljs-built_in">string</span>;
  country: <span class="hljs-built_in">string</span>;
};

<span class="hljs-keyword">type</span> UserWithAddress = User &amp; Address;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getUserDetails</span>(<span class="hljs-params">user: UserWithAddress</span>): <span class="hljs-title">string</span> </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-string">`<span class="hljs-subst">${user.name}</span> (<span class="hljs-subst">${user.age}</span> years old), lives in <span class="hljs-subst">${user.city}</span>, <span class="hljs-subst">${user.country}</span>`</span>;
}

<span class="hljs-keyword">const</span> user: UserWithAddress = {
  name: <span class="hljs-string">"Alice"</span>,
  age: <span class="hljs-number">30</span>,
  city: <span class="hljs-string">"New York"</span>,
  country: <span class="hljs-string">"USA"</span>
};

<span class="hljs-built_in">console</span>.log(getUserDetails(user));
<span class="hljs-comment">// Output: "Alice (30 years old), lives in New York, USA"</span>
</code></pre>
<p>In this case:</p>
<ul>
<li><p><code>UserWithAddress</code> is an intersection type, which means the <code>user</code> object must have both the properties of <code>User</code> and <code>Address</code>.</p>
</li>
<li><p>TypeScript checks that both <code>name</code> and <code>age</code> (from <code>User</code>), as well as <code>city</code> and <code>country</code> (from <code>Address</code>), are present in the object.</p>
</li>
</ul>
<p>If we missed any of these properties, TypeScript would show an error.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// This will result in a TypeScript error:</span>
<span class="hljs-keyword">const</span> incompleteUser: UserWithAddress = {
  name: <span class="hljs-string">"Alice"</span>,
  age: <span class="hljs-number">30</span>,
  city: <span class="hljs-string">"New York"</span>
}; <span class="hljs-comment">// Missing 'country'</span>
</code></pre>
<h3 id="heading-why-use-intersection-types">Why Use <strong>Intersection Types</strong>?</h3>
<p>Intersection types are useful in several scenarios. First, they let you extend existing types without modifying them, making the code more modular and flexible. They’re also useful when you need to merge multiple different structures into one, such as combining a <code>User</code> with an <code>Address</code> or <code>OrderDetails</code>. And you can easily see all the required properties that an object must have when you use intersection types.</p>
<h3 id="heading-type-aliases-vs-intersection-types">Type Aliases vs Intersection Types:</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Feature</td><td>Type Alias</td><td>Intersection Type</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Definition</strong></td><td>Defines a single type.</td><td>Combines multiple types into one.</td></tr>
<tr>
<td><strong>Use case</strong></td><td>Create reusable types for objects or primitives.</td><td>Combine multiple types, requiring all properties.</td></tr>
<tr>
<td><strong>Combining Types</strong></td><td>Not used for combining types.</td><td>Used to combine multiple types.</td></tr>
<tr>
<td><strong>Example</strong></td><td><code>type User = { name: string, age: number };</code></td><td><code>type UserWithAddress = User &amp; Address;</code></td></tr>
</tbody>
</table>
</div><h3 id="heading-when-to-use-each-one">When to Use Each One</h3>
<ul>
<li><p>Use type aliases when you want to define a <strong>single type</strong> for an object, function, or other data structure. They help with clarity, reuse, and type safety.</p>
</li>
<li><p>Use intersection types when you want to <strong>combine multiple types</strong> into one. It’s ideal for scenarios where an object needs to fulfill multiple contracts at once, such as when combining different types or extending the functionality of an existing type.</p>
</li>
</ul>
<p>By leveraging Type Alias and Intersection Types in TypeScript, your code becomes easier to understand, safer, and more maintainable. These features provide structure to your data, helping to catch bugs earlier.</p>
<h2 id="heading-interfaces-in-typescript">Interfaces in TypeScript</h2>
<p>In TypeScript, an <strong>interface</strong> is a way to define the structure of an object, describing its properties and their types. Interfaces are used to enforce type-checking in your code, ensuring that objects adhere to a specific structure. Similar to type aliases, interfaces make your code more readable, reusable, and maintainable.</p>
<h3 id="heading-what-is-an-interface">What is an Interface?</h3>
<p>An interface is a blueprint for an object, defining what properties and methods it should have. Interfaces can be used to define custom types for objects, functions, or classes.</p>
<p>Here’s a basic example:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> User {
  name: <span class="hljs-built_in">string</span>;
  age: <span class="hljs-built_in">number</span>;
  address: <span class="hljs-built_in">string</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getUserInfo</span>(<span class="hljs-params">user: User</span>): <span class="hljs-title">string</span> </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-string">`<span class="hljs-subst">${user.name}</span> (<span class="hljs-subst">${user.age}</span> years old) lives at <span class="hljs-subst">${user.address}</span>`</span>;
}

<span class="hljs-keyword">const</span> user: User = {
  name: <span class="hljs-string">"Alice"</span>,
  age: <span class="hljs-number">30</span>,
  address: <span class="hljs-string">"123 Main St"</span>,
};

<span class="hljs-built_in">console</span>.log(getUserInfo(user)); <span class="hljs-comment">// Output: Alice (30 years old) lives at 123 Main St</span>
</code></pre>
<p>In this example:</p>
<ul>
<li><p>The <code>User</code> interface defines the shape of the object.</p>
</li>
<li><p>Any object of type <code>User</code> must have <code>name</code>, <code>age</code>, and <code>address</code> properties with the specified types.</p>
</li>
<li><p>The <code>getUserInfo</code> function ensures the <code>user</code> parameter adheres to the <code>User</code> interface.</p>
</li>
</ul>
<h3 id="heading-similarities-between-interfaces-and-type-aliases">Similarities Between Interfaces and Type Aliases</h3>
<ul>
<li><p>Both interfaces and type aliases can define the structure of objects.</p>
</li>
<li><p>Both can be extended, though the syntax differs.</p>
</li>
<li><p>Both improve code readability and reusability.</p>
</li>
<li><p>In most cases, you can use interfaces or type aliases interchangeably to define object types.</p>
</li>
</ul>
<p>Example with a type alias:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> User = {
  name: <span class="hljs-built_in">string</span>;
  age: <span class="hljs-built_in">number</span>;
  address: <span class="hljs-built_in">string</span>;
};

<span class="hljs-keyword">const</span> user: User = {
  name: <span class="hljs-string">"Bob"</span>,
  age: <span class="hljs-number">25</span>,
  address: <span class="hljs-string">"456 Elm St"</span>,
};
</code></pre>
<p>Both the <code>type</code> and <code>interface</code> achieve the same result in this scenario.</p>
<h3 id="heading-differences-between-interfaces-and-type-aliases">Differences Between Interfaces and Type Aliases</h3>
<p>Let’s also summarize their key differences:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Feature</td><td>Interface</td><td>Type Alias</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Syntax</strong></td><td>Uses <code>interface</code> keyword.</td><td>Uses <code>type</code> keyword.</td></tr>
<tr>
<td><strong>Extensibility</strong></td><td>Can be extended using <code>extends</code>.</td><td>Can be extended using intersection (<code>&amp;</code>).</td></tr>
<tr>
<td><strong>Declaration Merging</strong></td><td>Supports merging across multiple declarations.</td><td>Does not support declaration merging.</td></tr>
<tr>
<td><strong>Union Types</strong></td><td>Cannot define union types.</td><td>Can define union types.</td></tr>
</tbody>
</table>
</div><h3 id="heading-extending-with-interfaces-and-type-aliases">Extending with Interfaces and Type Aliases</h3>
<p><strong>Extending Interfaces:</strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> Address {
  city: <span class="hljs-built_in">string</span>;
  country: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">interface</span> User <span class="hljs-keyword">extends</span> Address {
  name: <span class="hljs-built_in">string</span>;
  age: <span class="hljs-built_in">number</span>;
}

<span class="hljs-keyword">const</span> user: User = {
  name: <span class="hljs-string">"Alice"</span>,
  age: <span class="hljs-number">30</span>,
  city: <span class="hljs-string">"New York"</span>,
  country: <span class="hljs-string">"USA"</span>,
};
</code></pre>
<p><strong>Using Type Alias for Intersection:</strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> Address = {
  city: <span class="hljs-built_in">string</span>;
  country: <span class="hljs-built_in">string</span>;
};

<span class="hljs-keyword">type</span> User = {
  name: <span class="hljs-built_in">string</span>;
  age: <span class="hljs-built_in">number</span>;
} &amp; Address;

<span class="hljs-keyword">const</span> user: User = {
  name: <span class="hljs-string">"Alice"</span>,
  age: <span class="hljs-number">30</span>,
  city: <span class="hljs-string">"New York"</span>,
  country: <span class="hljs-string">"USA"</span>,
};
</code></pre>
<p>Both approaches result in the same outcome, but the syntax is different.</p>
<h3 id="heading-advanced-concepts-with-interfaces">Advanced Concepts with Interfaces</h3>
<p><strong>1. Optional Properties:</strong></p>
<p>Interfaces can define properties as optional using the <code>?</code> symbol:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> User {
  name: <span class="hljs-built_in">string</span>;
  age?: <span class="hljs-built_in">number</span>; <span class="hljs-comment">// Optional</span>
}

<span class="hljs-keyword">const</span> user1: User = { name: <span class="hljs-string">"Alice"</span> };
<span class="hljs-keyword">const</span> user2: User = { name: <span class="hljs-string">"Bob"</span>, age: <span class="hljs-number">25</span> };
</code></pre>
<p><strong>2. Readonly Properties:</strong></p>
<p>Use the <code>readonly</code> modifier to make properties immutable:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> User {
  <span class="hljs-keyword">readonly</span> id: <span class="hljs-built_in">number</span>;
  name: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">const</span> user: User = { id: <span class="hljs-number">1</span>, name: <span class="hljs-string">"Alice"</span> };
<span class="hljs-comment">// user.id = 2; // Error: Cannot assign to 'id' because it is a read-only property.</span>
</code></pre>
<p><strong>3. Function Types:</strong></p>
<p>Interfaces can define function signatures:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> Add {
  (a: <span class="hljs-built_in">number</span>, b: <span class="hljs-built_in">number</span>): <span class="hljs-built_in">number</span>;
}

<span class="hljs-keyword">const</span> add: Add = <span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> a + b;
<span class="hljs-built_in">console</span>.log(add(<span class="hljs-number">5</span>, <span class="hljs-number">3</span>)); <span class="hljs-comment">// Output: 8</span>
</code></pre>
<p><strong>4. Index Signatures:</strong></p>
<p>Interfaces can define dynamic property names:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> StringDictionary {
  [key: <span class="hljs-built_in">string</span>]: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">const</span> dictionary: StringDictionary = {
  hello: <span class="hljs-string">"world"</span>,
  name: <span class="hljs-string">"Alice"</span>,
};
</code></pre>
<p><strong>5. Extending Multiple Interfaces:</strong></p>
<p>An interface can extend multiple interfaces:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> A {
  propA: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">interface</span> B {
  propB: <span class="hljs-built_in">number</span>;
}

<span class="hljs-keyword">interface</span> C <span class="hljs-keyword">extends</span> A, B {
  propC: <span class="hljs-built_in">boolean</span>;
}

<span class="hljs-keyword">const</span> obj: C = {
  propA: <span class="hljs-string">"Hello"</span>,
  propB: <span class="hljs-number">42</span>,
  propC: <span class="hljs-literal">true</span>,
};
</code></pre>
<h3 id="heading-when-to-use-interfaces-vs-type-aliases">When to Use Interfaces vs. Type Aliases</h3>
<ul>
<li><p>Use <strong>interfaces</strong> when you need to define object shapes, especially if you plan to extend them. Also use interfaces if you need declaration merging, as type aliases don’t support it.</p>
</li>
<li><p>Use <strong>type aliases</strong> for more complex types, such as unions or intersections</p>
</li>
</ul>
<h2 id="heading-tuples-and-enums">Tuples and Enums</h2>
<p>A <strong>tuple</strong> in TypeScript is a special type of array that has a fixed number of elements, where each element can have a different type. Tuples ensure that the order and types of values remain consistent.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// A tuple with a string and a number</span>
<span class="hljs-keyword">let</span> user: [<span class="hljs-built_in">string</span>, <span class="hljs-built_in">number</span>] = [<span class="hljs-string">"Alice"</span>, <span class="hljs-number">25</span>];

<span class="hljs-built_in">console</span>.log(user[<span class="hljs-number">0</span>]); <span class="hljs-comment">// Output: Alice</span>
<span class="hljs-built_in">console</span>.log(user[<span class="hljs-number">1</span>]); <span class="hljs-comment">// Output: 25</span>
</code></pre>
<p>In this example, the tuple <code>user</code> contains a string (name) and a number (age). The order and types must be followed as defined.</p>
<h4 id="heading-tuple-with-optional-elements"><strong>Tuple with Optional Elements:</strong></h4>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> person: [<span class="hljs-built_in">string</span>, <span class="hljs-built_in">number</span>, <span class="hljs-built_in">boolean</span>?] = [<span class="hljs-string">"Bob"</span>, <span class="hljs-number">30</span>];

<span class="hljs-built_in">console</span>.log(person); <span class="hljs-comment">// Output: ["Bob", 30]</span>
</code></pre>
<p>Here, the third element (boolean) is optional.</p>
<h4 id="heading-tuple-with-read-only-property"><strong>Tuple with Read-Only Property:</strong></h4>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> coordinates: <span class="hljs-keyword">readonly</span> [<span class="hljs-built_in">number</span>, <span class="hljs-built_in">number</span>] = [<span class="hljs-number">10</span>, <span class="hljs-number">20</span>];

<span class="hljs-comment">// coordinates[0] = 50; // Error: Cannot assign to '0' because it is a read-only tuple</span>
</code></pre>
<p>The <code>readonly</code> keyword prevents modifying tuple values.</p>
<h3 id="heading-enums"><strong>Enums</strong></h3>
<p>An <strong>enum</strong> in TypeScript is a way to define a set of named constants. Enums make code more readable and help manage a fixed set of values.</p>
<h4 id="heading-numeric-enums-default"><strong>Numeric Enums (Default):</strong></h4>
<pre><code class="lang-typescript"><span class="hljs-built_in">enum</span> Status {
  Pending,   <span class="hljs-comment">// 0</span>
  InProgress, <span class="hljs-comment">// 1</span>
  Completed,  <span class="hljs-comment">// 2</span>
}

<span class="hljs-built_in">console</span>.log(Status.Pending);   <span class="hljs-comment">// Output: 0</span>
<span class="hljs-built_in">console</span>.log(Status.Completed); <span class="hljs-comment">// Output: 2</span>
</code></pre>
<p>By default, TypeScript assigns numeric values starting from <code>0</code>.</p>
<h4 id="heading-custom-number-values-in-enums"><strong>Custom Number Values in Enums:</strong></h4>
<pre><code class="lang-typescript"><span class="hljs-built_in">enum</span> OrderStatus {
  Pending = <span class="hljs-number">1</span>,
  Shipped = <span class="hljs-number">5</span>,
  Delivered = <span class="hljs-number">10</span>,
}

<span class="hljs-built_in">console</span>.log(OrderStatus.Shipped); <span class="hljs-comment">// Output: 5</span>
</code></pre>
<p>Here, custom values are assigned to each status.</p>
<h4 id="heading-string-enums"><strong>String Enums:</strong></h4>
<pre><code class="lang-typescript"><span class="hljs-built_in">enum</span> Direction {
  Up = <span class="hljs-string">"UP"</span>,
  Down = <span class="hljs-string">"DOWN"</span>,
  Left = <span class="hljs-string">"LEFT"</span>,
  Right = <span class="hljs-string">"RIGHT"</span>,
}

<span class="hljs-built_in">console</span>.log(Direction.Up); <span class="hljs-comment">// Output: "UP"</span>
</code></pre>
<p>String enums store fixed text values instead of numbers.</p>
<h4 id="heading-using-enums-in-a-function"><strong>Using Enums in a Function:</strong></h4>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getStatusText</span>(<span class="hljs-params">status: Status</span>): <span class="hljs-title">string</span> </span>{
  <span class="hljs-keyword">switch</span> (status) {
    <span class="hljs-keyword">case</span> Status.Pending:
      <span class="hljs-keyword">return</span> <span class="hljs-string">"Order is pending."</span>;
    <span class="hljs-keyword">case</span> Status.InProgress:
      <span class="hljs-keyword">return</span> <span class="hljs-string">"Order is in progress."</span>;
    <span class="hljs-keyword">case</span> Status.Completed:
      <span class="hljs-keyword">return</span> <span class="hljs-string">"Order is completed."</span>;
    <span class="hljs-keyword">default</span>:
      <span class="hljs-keyword">return</span> <span class="hljs-string">"Unknown status."</span>;
  }
}

<span class="hljs-built_in">console</span>.log(getStatusText(Status.InProgress)); <span class="hljs-comment">// Output: "Order is in progress."</span>
</code></pre>
<p>This function takes an enum value and returns a message based on the status.</p>
<p>Tuples define fixed-length arrays with different data types, while enums provide named constants for better readability, making your code more structured and type-safe.</p>
<h2 id="heading-type-assertion-type-unknown-and-type-never-in-typescript"><strong>Type Assertion, Type Unknown, and Type Never in TypeScript</strong></h2>
<h3 id="heading-type-assertion"><strong>Type Assertion</strong></h3>
<p>Type assertion tells TypeScript to treat a value as a specific type. It does not change the value but helps the compiler understand the type.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> value: unknown = <span class="hljs-string">"Hello, TypeScript!"</span>;

<span class="hljs-comment">// Using type assertion to treat 'value' as a string</span>
<span class="hljs-keyword">let</span> strLength: <span class="hljs-built_in">number</span> = (value <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>).length;

<span class="hljs-built_in">console</span>.log(strLength); <span class="hljs-comment">// Output: 18</span>
</code></pre>
<p>Here, <code>value</code> is initially <code>unknown</code>, but type assertion (<code>as string</code>) allows treating it as a string.</p>
<p>And here’s an alternative way to write type assertion:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> num = &lt;<span class="hljs-built_in">number</span>&gt;(<span class="hljs-number">10</span>);
<span class="hljs-built_in">console</span>.log(num); <span class="hljs-comment">// Output: 10</span>
</code></pre>
<p>The <code>&lt;number&gt;</code> syntax also performs type assertion.</p>
<h3 id="heading-type-unknown"><strong>Type Unknown</strong></h3>
<p>Let’s briefly revisit the <code>unknown</code> type now. Remember that it’s a safer alternative to <code>any</code> and can hold any value – but TypeScript requires type checking before using it.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> data: unknown;

data = <span class="hljs-string">"Hello"</span>;
data = <span class="hljs-number">42</span>;
data = <span class="hljs-literal">true</span>;

<span class="hljs-comment">// Type checking before using the value</span>
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> data === <span class="hljs-string">"string"</span>) {
  <span class="hljs-built_in">console</span>.log(data.toUpperCase()); <span class="hljs-comment">// Works only if data is a string</span>
}
</code></pre>
<p>Since <code>data</code> is <code>unknown</code>, TypeScript does not allow direct operations without checking its type first.</p>
<h3 id="heading-type-never"><strong>Type Never</strong></h3>
<p>The <code>never</code> type represents values that never occur. It is often used for functions that never return or always throw an error.</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">throwError</span>(<span class="hljs-params">message: <span class="hljs-built_in">string</span></span>): <span class="hljs-title">never</span> </span>{
  <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(message);
}

<span class="hljs-comment">// throwError("Something went wrong!"); // This function never returns</span>
</code></pre>
<p>Here, <code>throwError</code> does not return anything because it always throws an error.</p>
<h4 id="heading-example-of-type-never-in-a-switch-case"><strong>Example of Type Never in a Switch Case:</strong></h4>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> Status = <span class="hljs-string">"success"</span> | <span class="hljs-string">"failure"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">checkStatus</span>(<span class="hljs-params">status: Status</span>): <span class="hljs-title">void</span> </span>{
  <span class="hljs-keyword">switch</span> (status) {
    <span class="hljs-keyword">case</span> <span class="hljs-string">"success"</span>:
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Operation was successful."</span>);
      <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">case</span> <span class="hljs-string">"failure"</span>:
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Operation failed."</span>);
      <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">default</span>:
      <span class="hljs-keyword">const</span> unexpected: <span class="hljs-built_in">never</span> = status; <span class="hljs-comment">// Ensures all cases are handled</span>
  }
}
</code></pre>
<p>This ensures that all possible values of <code>Status</code> are handled, preventing unexpected behavior.</p>
<p>Here’s a quick comparison of these different approaches:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Feature</strong></td><td><strong>Description</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>Type Assertion</strong></td><td>Tells TypeScript to treat a value as a specific type.</td></tr>
<tr>
<td><strong>Unknown Type</strong></td><td>Allows storing any value but requires type checking before use.</td></tr>
<tr>
<td><strong>Never Type</strong></td><td>Represents values that never occur, used for functions that never return.</td></tr>
</tbody>
</table>
</div><h2 id="heading-generics-in-typescript">Generics in TypeScript</h2>
<p>Generics allow writing flexible, reusable, and type-safe code. Instead of specifying a fixed type, generics let a function, class, or interface work with multiple types while maintaining type safety.</p>
<h3 id="heading-basic-generics"><strong>Basic Generics</strong></h3>
<p>A generic function works with any type while keeping type safety.</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">identity</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params">value: T</span>): <span class="hljs-title">T</span> </span>{
  <span class="hljs-keyword">return</span> value;
}

<span class="hljs-built_in">console</span>.log(identity&lt;<span class="hljs-built_in">string</span>&gt;(<span class="hljs-string">"Hello"</span>)); <span class="hljs-comment">// Output: "Hello"</span>
<span class="hljs-built_in">console</span>.log(identity&lt;<span class="hljs-built_in">number</span>&gt;(<span class="hljs-number">42</span>));      <span class="hljs-comment">// Output: 42</span>
</code></pre>
<p>Here, <code>&lt;T&gt;</code> is a <strong>generic type parameter</strong>, allowing <code>identity</code> to work with any type.</p>
<h3 id="heading-generics-with-arrays"><strong>Generics with Arrays</strong></h3>
<p>Generics help enforce type safety in arrays.</p>
<p>Here’s an example of reversing an array with generics:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">reverseArray</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params">arr: T[]</span>): <span class="hljs-title">T</span>[] </span>{
  <span class="hljs-keyword">return</span> arr.reverse();
}

<span class="hljs-built_in">console</span>.log(reverseArray&lt;<span class="hljs-built_in">number</span>&gt;([<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]));  <span class="hljs-comment">// Output: [3, 2, 1]</span>
<span class="hljs-built_in">console</span>.log(reverseArray&lt;<span class="hljs-built_in">string</span>&gt;([<span class="hljs-string">"A"</span>, <span class="hljs-string">"B"</span>, <span class="hljs-string">"C"</span>])); <span class="hljs-comment">// Output: ["C", "B", "A"]</span>
</code></pre>
<p>This ensures that the function always returns the same type of array it receives.</p>
<h3 id="heading-generics-with-interfaces"><strong>Generics with Interfaces</strong></h3>
<p>Generics can be used in interfaces to define flexible object structures.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> StorageBox&lt;T&gt; {
  content: T;
}

<span class="hljs-keyword">let</span> numberBox: StorageBox&lt;<span class="hljs-built_in">number</span>&gt; = { content: <span class="hljs-number">100</span> };
<span class="hljs-keyword">let</span> stringBox: StorageBox&lt;<span class="hljs-built_in">string</span>&gt; = { content: <span class="hljs-string">"TypeScript"</span> };

<span class="hljs-built_in">console</span>.log(numberBox.content); <span class="hljs-comment">// Output: 100</span>
<span class="hljs-built_in">console</span>.log(stringBox.content); <span class="hljs-comment">// Output: "TypeScript"</span>
</code></pre>
<p>Here, <code>StorageBox&lt;T&gt;</code> allows storing different types while ensuring consistency.</p>
<h3 id="heading-generics-with-classes"><strong>Generics with Classes</strong></h3>
<p>Generics also work in classes, making them more reusable.</p>
<p>Here’s an example of a generic queue class:</p>
<pre><code class="lang-typescript">lass Queue&lt;T&gt; {
  <span class="hljs-keyword">private</span> items: T[] = [];

  enqueue(item: T): <span class="hljs-built_in">void</span> {
    <span class="hljs-built_in">this</span>.items.push(item);
  }

  dequeue(): T | <span class="hljs-literal">undefined</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.items.shift();
  }
}

<span class="hljs-keyword">let</span> numberQueue = <span class="hljs-keyword">new</span> Queue&lt;<span class="hljs-built_in">number</span>&gt;();
numberQueue.enqueue(<span class="hljs-number">10</span>);
numberQueue.enqueue(<span class="hljs-number">20</span>);
<span class="hljs-built_in">console</span>.log(numberQueue.dequeue()); <span class="hljs-comment">// Output: 10</span>

<span class="hljs-keyword">let</span> stringQueue = <span class="hljs-keyword">new</span> Queue&lt;<span class="hljs-built_in">string</span>&gt;();
stringQueue.enqueue(<span class="hljs-string">"Hello"</span>);
stringQueue.enqueue(<span class="hljs-string">"World"</span>);
<span class="hljs-built_in">console</span>.log(stringQueue.dequeue()); <span class="hljs-comment">// Output: "Hello"</span>
</code></pre>
<p>This class works with any type while maintaining type safety.</p>
<h3 id="heading-generics-with-multiple-type-parameters"><strong>Generics with Multiple Type Parameters</strong></h3>
<p>A function or class can accept more than one generic type.</p>
<p>Here’s an example of a function that swaps two values:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">swap</span>&lt;<span class="hljs-title">T</span>, <span class="hljs-title">U</span>&gt;(<span class="hljs-params">first: T, second: U</span>): [<span class="hljs-title">U</span>, <span class="hljs-title">T</span>] </span>{
  <span class="hljs-keyword">return</span> [second, first];
}

<span class="hljs-built_in">console</span>.log(swap&lt;<span class="hljs-built_in">string</span>, <span class="hljs-built_in">number</span>&gt;(<span class="hljs-string">"Age"</span>, <span class="hljs-number">25</span>)); <span class="hljs-comment">// Output: [25, "Age"]</span>
<span class="hljs-built_in">console</span>.log(swap&lt;<span class="hljs-built_in">boolean</span>, <span class="hljs-built_in">string</span>&gt;(<span class="hljs-literal">true</span>, <span class="hljs-string">"Yes"</span>)); <span class="hljs-comment">// Output: ["Yes", true]</span>
</code></pre>
<p>Here, <code>&lt;T, U&gt;</code> allows the function to work with different types at the same time.</p>
<h3 id="heading-generics-with-constraints"><strong>Generics with Constraints</strong></h3>
<p>Sometimes, a generic type should follow certain rules. <strong>Constraints</strong> ensure that a type has specific properties.</p>
<p>Here’s an example of ensuring that a type has a <code>length</code> property:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getLength</span>&lt;<span class="hljs-title">T</span> <span class="hljs-title">extends</span> </span>{ length: <span class="hljs-built_in">number</span> }&gt;(item: T): <span class="hljs-built_in">number</span> {
  <span class="hljs-keyword">return</span> item.length;
}

<span class="hljs-built_in">console</span>.log(getLength(<span class="hljs-string">"Hello"</span>));   <span class="hljs-comment">// Output: 5</span>
<span class="hljs-built_in">console</span>.log(getLength([<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>])); <span class="hljs-comment">// Output: 3</span>
</code></pre>
<p>Here, <code>T extends { length: number }</code> ensures that <code>T</code> has a <code>length</code> property.</p>
<h3 id="heading-advanced-generics-with-the-keyof-operator"><strong>Advanced: Generics with the</strong> <code>keyof</code> <strong>Operator</strong></h3>
<p>The <code>keyof</code> operator can be used to ensure valid property names.</p>
<p>Here’s an example of getting a property value by name:</p>
<pre><code class="lang-typescript">typescriptCopyEditfunction getProperty&lt;T, K <span class="hljs-keyword">extends</span> keyof T&gt;(obj: T, key: K): T[K] {
  <span class="hljs-keyword">return</span> obj[key];
}

<span class="hljs-keyword">let</span> user = { name: <span class="hljs-string">"Alice"</span>, age: <span class="hljs-number">30</span> };

<span class="hljs-built_in">console</span>.log(getProperty(user, <span class="hljs-string">"name"</span>)); <span class="hljs-comment">// Output: "Alice"</span>
<span class="hljs-built_in">console</span>.log(getProperty(user, <span class="hljs-string">"age"</span>));  <span class="hljs-comment">// Output: 30</span>
</code></pre>
<p>Here, <code>K extends keyof T</code> ensures that <code>key</code> is a valid property of <code>T</code>.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this handbook, you got an in-depth overview of how you can use TypeScript basics in React. We discussed important concepts like type annotations, type inference, and managing objects and arrays, showing how TypeScript improves code stability and maintenance.</p>
<p>We also covered some advanced topics such as union and any types, readonly properties, and the use of generics, type aliases, and interfaces. I hope the examples helped you understand how TypeScript can enhance your JavaScript development, making TS a valuable tool for building robust, large-scale applications.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Start Learning TypeScript – A Beginner's Guide ]]>
                </title>
                <description>
                    <![CDATA[ JavaScript is the most widely-used programming language for web development. But it lacks type-checking support, which is an essential feature of modern programming languages. JavaScript was originally designed as a simple scripting language. Its loo... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/start-learning-typescript-beginners-guide/</link>
                <guid isPermaLink="false">6792ea9437616d62ca07fe03</guid>
                
                    <category>
                        <![CDATA[ TypeScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Beginner Developers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Programming Blogs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Technical writing  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Akande Olalekan Toheeb ]]>
                </dc:creator>
                <pubDate>Fri, 24 Jan 2025 01:19:16 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1737681395105/19aeca8f-e763-4833-9ac3-5c4db7d12fe7.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>JavaScript is the most widely-used programming language for web development. But it lacks type-checking support, which is an essential feature of modern programming languages.</p>
<p>JavaScript was originally designed as a simple scripting language. Its loose nature and absence of crucial <strong>Object-Oriented Programming (OOP)</strong> features pose certain challenges for developers:</p>
<ol>
<li><p>Limited documentation and auto-completion.</p>
</li>
<li><p>Inability to utilise OOP concepts.</p>
</li>
<li><p>Lack of type safety, leading to runtime errors.</p>
</li>
<li><p>Challenges in refactoring and maintenance.</p>
</li>
<li><p>Absence of interfaces and integration points.</p>
</li>
</ol>
<p>TypeScript solves these problems. It was built to make JavaScript a more perfect modern programming language. It helps improve the developer experience, offers many useful features, and improves interoperability.</p>
<p>This article dives into TypeScript basics. I’ll teach you how to install TS and set up a project. Then we’ll cover some important fundamentals. You’ll also learn how TypeScript compiles into JavaScript, making it compatible with browsers and Node.js environments.</p>
<h3 id="heading-what-well-coverheading-what-well-cover"><a class="post-section-overview" href="#heading-what-well-cover">What we’ll cover:</a></h3>
<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-how-to-install-typescript">Getting Started – How to Install TypeScript</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-organize-your-typescript-projects">How to Organize Your TypeScript Projects</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-typing-works-in-typescript">How Typing Works in TypeScript</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-typing-techniques">Typing Techniques</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-static-vs-dynamic-typing-in-typescript">Static vs. Dynamic Typing in TypeScript</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-type-inference-and-union-types">Type Inference and Union Types</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-handle-objects-arrays-and-function-types-in-typescript">How to Handle Objects, Arrays, and Function Types in TypeScript</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-object-types-in-typescript">Object Types in TypeScript</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-array-types-in-typescript">Array Types in TypeScript</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-arrays-in-typescript">How to Use Arrays in TypeScript</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-function-types-in-typescript">Function Types in TypeScript</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-custom-types-in-typescript">How to Create Custom Types in TypeScript</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-the-type-keyword">The Type Keyword</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-typescript-interfaces">TypeScript Interfaces</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-when-to-use-interfaces">When to Use Interfaces</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-generics-and-literal-types">Generics and Literal Types</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-merge-types-in-typescript">How to Merge Types in TypeScript</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-when-to-use-each-approach">When to Use Each Approach</a></li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-bundling-and-transformations-in-typescript">Bundling and Transformations in TypeScript</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-building-better-code-with-typescript">Building Better Code with TypeScript</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before diving into TypeScript, it's important to have a foundational understanding of certain concepts to ensure a smoother learning journey. While TypeScript enhances JavaScript with static typing and other powerful features, it builds on core JavaScript principles. Here's what you should know:</p>
<h4 id="heading-1-javascript-fundamentals"><strong>1. JavaScript Fundamentals</strong></h4>
<p>TypeScript is a superset of JavaScript, meaning it extends JavaScript's capabilities. To effectively learn TypeScript, you should first have a solid grasp of JavaScript basics, including:</p>
<ul>
<li><p><strong>Syntax and data types:</strong> Understand how to declare variables (<code>let</code>, <code>const</code>, and <code>var</code>), work with primitive types (strings, numbers, booleans), and manage arrays and objects.</p>
</li>
<li><p><strong>Control flow:</strong> Be familiar with loops (<code>for</code>, <code>while</code>), conditionals (<code>if-else</code>, <code>switch</code>), and how they control program execution.</p>
</li>
<li><p><strong>Functions:</strong> Know how to define and invoke functions, work with parameters, return values, and understand concepts like arrow functions and closures.</p>
</li>
<li><p><strong>Object-Oriented Programming (OOP):</strong> Learn about creating and working with objects, classes, and inheritance. TypeScript's class-based features build heavily on JavaScript's OOP model.</p>
</li>
<li><p><strong>Error handling:</strong> Understand how to use <code>try-catch</code> blocks to handle runtime errors.</p>
</li>
</ul>
<h4 id="heading-2-basic-html-and-css"><strong>2. Basic HTML and CSS</strong></h4>
<p>Although TypeScript is a language used primarily with JavaScript, having a basic understanding of HTML and CSS is helpful, especially for front-end developers. This is because most TypeScript projects involve creating or working with web applications</p>
<ul>
<li><p><strong>HTML:</strong> Understand how to structure web pages using tags, attributes, and elements.</p>
</li>
<li><p><strong>CSS:</strong> Learn how to style elements using selectors, properties, and values. Familiarity with CSS frameworks like Bootstrap is a bonus.</p>
</li>
</ul>
<h4 id="heading-3-familiarity-with-development-tools"><strong>3. Familiarity with Development Tools</strong></h4>
<ul>
<li><p><strong>A code editor</strong> like Visual Studio Code, which has excellent TypeScript support and extensions.</p>
</li>
<li><p><strong>Node.js and npm:</strong> Understand how to set up a development environment, run JavaScript outside the browser, and use npm (Node Package Manager) to install dependencies.</p>
</li>
<li><p><strong>Version control (Git):</strong> Learn the basics of Git to track changes and collaborate effectively on TypeScript projects.</p>
</li>
</ul>
<h2 id="heading-getting-started-how-to-install-typescript">Getting Started – How to Install TypeScript</h2>
<p>To get started working with TypeScript you’ll need to install it. It’s not a complicated process. With TypeScript installed, you can leverage its power to create high-quality solutions.</p>
<p>You can install TS in two ways:</p>
<ol>
<li><strong>Global Installation</strong>: enables you to access the compiler from any directory on your machine. To install TypeScript globally, execute the following command:</li>
</ol>
<pre><code class="lang-bash">npm install -g typescript
</code></pre>
<p>This command leverages the Node.js package manager, <code>npm</code>. It installs TypeScript globally, making the command available in the command line.</p>
<ol start="2">
<li><strong>Local Installation</strong>: in this case, TypeScript is installed only within a specific project. This method ensures version compatibility and consistency across team members. To install TypeScript locally, execute the following command:</li>
</ol>
<pre><code class="lang-bash">npm install typescript --save-dev
</code></pre>
<p>Different from global installation, this command installs TypeScript as a development dependency. The <code>tsc</code> command is only available for project-specific usage, that is the specific project where you run the command.</p>
<p><strong>Can you seamlessly install TypeScript now? I hope so!</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1737293526617/2f630f4c-c74f-4525-a291-9febf06d8d8b.gif" alt="2f630f4c-c74f-4525-a291-9febf06d8d8b" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-organize-your-typescript-projects">How to Organize Your TypeScript Projects</h2>
<p>Organizing a TypeScript project involves structuring its files with meaningful names and directories, separating concerns, and using modules for encapsulation and reusability.</p>
<p>The <code>.ts</code> extension denotes typeScript files and contains code that converts into JavaScript for execution.</p>
<p>TypeScript also supports <code>.d.ts</code> files, also known as type definition files. These files offer type information about external JavaScript libraries or modules, aiding in better type-checking and code completion as well as improving development efficiency. Below is an example of a good TS project structure:</p>
<pre><code class="lang-plaintext">my-ts-project/
├── src/ 
│   ├── components/ 
│   │   ├── Button.tsx
│   │   ├── Input.tsx
│   │   └── Modal.tsx
│   ├── services/ 
│   │   ├── api.ts
│   │   └── authService.ts
│   ├── utils/ 
│   │   ├── helpers.ts 
│   │   └── validators.ts
│   ├── models/ 
│   │   ├── User.ts
│   │   └── Product.ts
│   ├── index.tsx 
│   └── styles/ 
│       ├── global.css
│       └── theme.css
├── public/ 
│   ├── index.html
│   └── assets/ 
│       ├── images/
│       └── fonts/
├── tsconfig.json
└── package.json
</code></pre>
<p>Let’s understand what’s going on here:</p>
<ol>
<li><p><code>src/</code>: This directory houses all the source code for the project.</p>
<ul>
<li><p><code>components/</code>: Contains reusable UI components (for example, <code>Button</code>, <code>Input</code>, <code>Modal</code>). Using <code>.tsx</code> (TypeScript JSX) allows you to write JSX with type safety.</p>
</li>
<li><p><code>services/</code>: Holds services that interact with external APIs or handle application logic (for example, <code>api.ts</code> for API calls, <code>authService.ts</code> for authentication).</p>
</li>
<li><p><code>utils/</code>: Contains helper functions and utility classes for common tasks (for example, <code>helpers.ts</code> for date formatting, <code>validators.ts</code> for input validation).</p>
</li>
<li><p><code>models/</code>: Defines TypeScript interfaces or classes to represent data structures (for example, <code>User.ts</code>, <code>Product.ts</code>).</p>
</li>
<li><p><code>index.tsx</code>: The main entry point of the application.</p>
</li>
<li><p><code>styles/</code>: Contains CSS or other styling files.</p>
</li>
</ul>
</li>
<li><p><code>public/</code>: This directory contains static assets that are not processed by TypeScript (for example, HTML, images, fonts).</p>
</li>
<li><p><code>tsconfig.json</code>: The TypeScript configuration file, specifying compiler options.</p>
</li>
<li><p><code>package.json</code>: The project's manifest file, listing dependencies, scripts, and other project metadata.</p>
</li>
</ol>
<p>Just a quick note about naming conventions so you understand them here:</p>
<ul>
<li><p>Use PascalCase for class names (for example, <code>User</code>, <code>Product</code>).</p>
</li>
<li><p>Use camelCase for function names and variable names (for example, <code>getUser</code>, <code>firstName</code>).</p>
</li>
<li><p>Use meaningful and descriptive names for files and directories.</p>
</li>
</ul>
<p>This structure promotes modularity, reusability, and better organization, making your TypeScript projects easier to maintain and scale.</p>
<p>Properly organizing your TS projects enhances code maintainability, readability, and collaboration in TypeScript development workflows.</p>
<h2 id="heading-how-typing-works-in-typescript">How Typing Works in TypeScript</h2>
<p>Like any other typed programming language, TypeScript relies on type definitions, generally called <strong>Typing</strong>.</p>
<p>Typing is a term used in programming to define data types for variables, method parameters, and return values within the code.</p>
<p>Typing allows you to catch errors quickly and early in development, a superpower that helps maintain better code quality.</p>
<p>To specify a type in TypeScript, put a colon( <code>:</code>) and the desired data type after your variable name. Here’s an example:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> age: <span class="hljs-built_in">number</span> = <span class="hljs-number">2</span>;
</code></pre>
<p>The above variable is declared with the <code>number</code> type. In TypeScript, this means it can store numbers only and nothing else.</p>
<h3 id="heading-typing-techniques">Typing Techniques</h3>
<p>In TypeScript, data can be typed in two main ways:</p>
<ol>
<li><strong>Static Typing</strong>: Static typing refers to explicitly specifying the data type of variables and other entities in the code during development. The TypeScript compiler enforces these type definitions, helping to catch type-related errors early. For example:</li>
</ol>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> age: <span class="hljs-built_in">number</span> = <span class="hljs-number">25</span>;
</code></pre>
<p>Here, the variable <code>age</code> is explicitly declared to have the <code>number</code> type. This ensures that only numeric values can be assigned to it, reducing the risk of runtime errors.</p>
<ol start="2">
<li><strong>Dynamic Typing</strong>: Dynamic typing in TypeScript refers to scenarios where the type of a variable is determined at runtime. This can occur when variables are assigned the <code>any</code> type, which allows them to hold values of any type. TypeScript does not perform type-checking on operations involving variables with the <code>any</code> type.</li>
</ol>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> value: <span class="hljs-built_in">any</span>;
value = <span class="hljs-number">25</span>; <span class="hljs-comment">// Number</span>
value = <span class="hljs-string">"Hello"</span>; <span class="hljs-comment">// String</span>
</code></pre>
<p>While TypeScript is primarily a statically typed language, dynamic typing can still be useful in specific cases, such as:</p>
<ul>
<li><p>Working with third-party libraries that lack type definitions.</p>
</li>
<li><p>Interfacing with dynamically structured data (for example, JSON responses from APIs with unknown structures).</p>
</li>
<li><p>Rapid prototyping or when type information is unavailable during the initial development phase.</p>
</li>
</ul>
<h3 id="heading-static-vs-dynamic-typing-in-typescript">Static vs. Dynamic Typing in TypeScript</h3>
<p>Static typing is significantly more common in TypeScript, as it is one of the core features that sets TypeScript apart from JavaScript. By enforcing strict type checks, static typing enhances code maintainability, reduces bugs, and improves developer productivity.</p>
<p>Dynamic typing is typically reserved for cases where flexibility is required or when dealing with data whose structure cannot be determined in advance. Just keep in mind that relying heavily on dynamic typing (for example, overusing the <code>any</code> type) is generally discouraged, as it undermines the benefits of TypeScript's static typing system.</p>
<p>So while dynamic typing has its place in certain edge cases, static typing is the preferred and more commonly used approach in TypeScript development.</p>
<h3 id="heading-type-inference-and-union-types">Type Inference and Union Types</h3>
<h4 id="heading-type-inference"><strong>Type Inference</strong></h4>
<p>Type inference is a powerful TypeScript feature that allows the compiler to automatically deduce the type of a variable based on the value assigned to it during initialization. In simpler terms, TypeScript looks at the value you assign to a variable and decides what type it should be, even if you don’t explicitly declare the type.</p>
<p>For example:</p>
<pre><code class="lang-typescript">typescriptCopyEditlet age = <span class="hljs-number">25</span>; <span class="hljs-comment">// TypeScript infers that 'age' is of type 'number'</span>
age = <span class="hljs-string">"hello"</span>; <span class="hljs-comment">// Error: Type 'string' is not assignable to type 'number'</span>
</code></pre>
<p>In this example, the <code>age</code> variable is automatically inferred as a <code>number</code> because of its initial value, <code>25</code>. Any attempt to reassign <code>age</code> to a value of a different type (like a string) will result in a type error.</p>
<p>Type inference is particularly useful because it reduces the need for explicit type annotations, making your code cleaner and more readable. However, it still provides the safety and reliability of TypeScript's type-checking.</p>
<h5 id="heading-when-to-use-type-inference">When to use type inference:</h5>
<ul>
<li><p><strong>Simple assignments</strong>: Use type inference for straightforward assignments where the type is obvious from the value.</p>
</li>
<li><p><strong>Default values</strong>: When providing default values for variables or function parameters, type inference ensures the correct type is applied without requiring manual annotations.</p>
</li>
<li><p><strong>Rapid prototyping</strong>: During early stages of development, type inference can reduce boilerplate code while still enforcing type safety.</p>
</li>
</ul>
<h4 id="heading-union-types"><strong>Union Types</strong></h4>
<p>Union types allow a variable to hold values of multiple types. They are defined by placing a pipe (<code>|</code>) between the types. This feature is particularly useful when a variable may legitimately have more than one type during its lifecycle.</p>
<p>For example:</p>
<pre><code class="lang-typescript">typescriptCopyEditlet numOrString: <span class="hljs-built_in">number</span> | <span class="hljs-built_in">string</span>; <span class="hljs-comment">// 'numOrString' can hold either a number or a string</span>
numOrString = <span class="hljs-number">25</span>; <span class="hljs-comment">// Valid</span>
numOrString = <span class="hljs-string">"hello"</span>; <span class="hljs-comment">// Valid</span>
numOrString = <span class="hljs-literal">true</span>; <span class="hljs-comment">// Error: Type 'boolean' is not assignable to type 'number | string'</span>
</code></pre>
<p>You can even define union types with more than two possible types:</p>
<pre><code class="lang-typescript">typescriptCopyEditlet multiType: <span class="hljs-built_in">number</span> | <span class="hljs-built_in">string</span> | <span class="hljs-built_in">boolean</span>;
multiType = <span class="hljs-number">42</span>; <span class="hljs-comment">// Valid</span>
multiType = <span class="hljs-string">"TypeScript"</span>; <span class="hljs-comment">// Valid</span>
multiType = <span class="hljs-literal">false</span>; <span class="hljs-comment">// Valid</span>
</code></pre>
<h5 id="heading-when-to-use-union-types">When to use union types:</h5>
<ul>
<li><p><strong>Flexible function parameters</strong>: When a function can accept multiple types of input.</p>
<pre><code class="lang-typescript">  typescriptCopyEditfunction printValue(value: <span class="hljs-built_in">string</span> | <span class="hljs-built_in">number</span>) {
    <span class="hljs-built_in">console</span>.log(value);
  }
</code></pre>
</li>
<li><p><strong>Handling diverse data structures</strong>: When working with APIs or external data sources where fields may vary in type.</p>
</li>
<li><p><strong>Optional or multi-state variables</strong>: For example, a variable that can represent a loading state as a boolean, an error as a string, or valid data as an object:</p>
<pre><code class="lang-typescript">  typescriptCopyEditlet status: <span class="hljs-built_in">boolean</span> | <span class="hljs-built_in">string</span> | { success: <span class="hljs-built_in">boolean</span>; data: <span class="hljs-built_in">any</span> };
</code></pre>
</li>
</ul>
<h2 id="heading-how-to-handle-objects-arrays-and-function-types-in-typescript">How to Handle Objects, Arrays, and Function Types in TypeScript</h2>
<p>To master TypeScript, you must understand the various data types supported in TypeScript and how and when to use them.</p>
<p>The <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#primitive_values">JavaScript primitive types</a> such as <em>strings</em>, <em>numbers</em>, <em>booleans</em>, and more also define the fundamental building blocks of data in TypeScript. But in particular, <code>Objects</code><em>,</em> <code>Arrays</code><em>,</em> and <code>Functions</code> are essential for building robust applications. With objects, arrays, and functions, you can better handle data and use them efficiently in development.</p>
<h3 id="heading-object-types-in-typescript">Object Types in TypeScript</h3>
<p>Object types represent the blueprint for creating objects in TypeScript. You can use objects to define their shape, similar to how <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes">classes</a> are used in <strong>object-oriented programming (OOP)</strong>. But objects lack the behavioral aspects and encapsulation that classes offer.</p>
<p>To define an object type, explicitly define the object's blueprint after the colon(<code>:</code>). For Example:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Object Type Initialization</span>

<span class="hljs-keyword">let</span> student: {
    name: <span class="hljs-built_in">string</span>;
    age: <span class="hljs-built_in">number</span>;
    matricNumber: <span class="hljs-built_in">string</span> | <span class="hljs-built_in">number</span>;
 };

<span class="hljs-comment">// Assigning the Object with actual data</span>

student = {
    name: <span class="hljs-string">"Akande"</span>
    age: <span class="hljs-number">21</span>,
    matricNumber: <span class="hljs-number">21</span>/<span class="hljs-number">52</span> + <span class="hljs-string">"HP"</span> + <span class="hljs-number">19</span>,
};
</code></pre>
<p>Notice that the properties end with a semi-colon<code>;</code> instead of a comma <code>,</code> which ends them in an actual object.</p>
<p>The above is the primary way to define an object in TypeScript. Another way is to use <code>interfaces</code>, which I’ll cover later in this article.</p>
<h3 id="heading-array-types-in-typescript">Array Types in TypeScript</h3>
<p>Arrays in TypeScript allow you to store multiple values of the same or different data types in a single variable. They enhance the safety and clarity of your code by enforcing type consistency across the array elements.</p>
<p>In TypeScript, array types can be defined in two ways:</p>
<h4 id="heading-1-using-the-array-model"><strong>1. Using the</strong> <code>Array&lt;type&gt;</code> model</h4>
<p>This syntax uses the generic <code>Array</code> type, where <code>type</code> represents the type of elements the array can hold.</p>
<pre><code class="lang-typescript">typescriptCopyEditlet numbers: <span class="hljs-built_in">Array</span>&lt;<span class="hljs-built_in">number</span>&gt; = [<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-keyword">let</span> mixedArray: <span class="hljs-built_in">Array</span>&lt;<span class="hljs-built_in">number</span> | <span class="hljs-built_in">string</span>&gt; = [<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-string">"Hello"</span>];
</code></pre>
<ul>
<li><p><code>numbers</code> Example: This array can only contain numbers. Attempting to add a string or other type to this array will result in a type error.</p>
<pre><code class="lang-typescript">  typescriptCopyEditnumbers.push(<span class="hljs-number">6</span>); <span class="hljs-comment">// Valid</span>
  numbers.push(<span class="hljs-string">"Hello"</span>); <span class="hljs-comment">// Error: Type 'string' is not assignable to type 'number'</span>
</code></pre>
</li>
<li><p><code>mixedArray</code> Example: This array uses a union type (<code>number | string</code>), allowing it to store both numbers and strings.</p>
<pre><code class="lang-typescript">  typescriptCopyEditmixedArray.push(<span class="hljs-number">42</span>); <span class="hljs-comment">// Valid</span>
  mixedArray.push(<span class="hljs-string">"TypeScript"</span>); <span class="hljs-comment">// Valid</span>
  mixedArray.push(<span class="hljs-literal">true</span>); <span class="hljs-comment">// Error: Type 'boolean' is not assignable to type 'number | string'</span>
</code></pre>
</li>
</ul>
<h4 id="heading-2-using-the-type-model"><strong>2. Using the</strong> <code>type[]</code> model</h4>
<p>This syntax appends square brackets (<code>[]</code>) to the type of elements the array can hold.</p>
<pre><code class="lang-typescript">typescriptCopyEditconst numbers: <span class="hljs-built_in">number</span>[] = [<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-keyword">const</span> mixedArray: (<span class="hljs-built_in">string</span> | <span class="hljs-built_in">number</span>)[] = [<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-string">"Hello"</span>];
</code></pre>
<ul>
<li><p><code>numbers</code> Example: Similar to the <code>Array&lt;number&gt;</code> example, this array can only hold numbers.</p>
<pre><code class="lang-typescript">  typescriptCopyEditnumbers[<span class="hljs-number">0</span>] = <span class="hljs-number">10</span>; <span class="hljs-comment">// Valid</span>
  numbers.push(<span class="hljs-string">"Hi"</span>); <span class="hljs-comment">// Error: Type 'string' is not assignable to type 'number'</span>
</code></pre>
</li>
<li><p><code>mixedArray</code> Example: Like the earlier <code>mixedArray</code>, this array allows both numbers and strings, providing flexibility where the type of data may vary.</p>
<pre><code class="lang-typescript">  typescriptCopyEditmixedArray[<span class="hljs-number">1</span>] = <span class="hljs-string">"World"</span>; <span class="hljs-comment">// Valid</span>
  mixedArray.push(<span class="hljs-literal">true</span>); <span class="hljs-comment">// Error: Type 'boolean' is not assignable to type 'string | number'</span>
</code></pre>
</li>
</ul>
<h3 id="heading-how-to-use-arrays-in-typescript"><strong>How to Use Arrays in TypeScript</strong></h3>
<p>Arrays are versatile and commonly used for storing collections of related data. Here are a few practical scenarios:</p>
<p><strong>Storing Homogeneous Data:</strong><br>When all elements in the array share the same type, such as a list of user IDs or product prices:</p>
<pre><code class="lang-typescript">typescriptCopyEditconst userIds: <span class="hljs-built_in">number</span>[] = [<span class="hljs-number">101</span>, <span class="hljs-number">102</span>, <span class="hljs-number">103</span>];
<span class="hljs-keyword">const</span> productPrices: <span class="hljs-built_in">Array</span>&lt;<span class="hljs-built_in">number</span>&gt; = [<span class="hljs-number">29.99</span>, <span class="hljs-number">49.99</span>, <span class="hljs-number">19.99</span>];
</code></pre>
<p><strong>Storing Heterogeneous Data:</strong><br>When elements can have different types, such as a list of messages containing text and optional metadata:</p>
<pre><code class="lang-typescript">typescriptCopyEditconst messages: (<span class="hljs-built_in">string</span> | <span class="hljs-built_in">object</span>)[] = [
  <span class="hljs-string">"Welcome"</span>,
  { <span class="hljs-keyword">type</span>: <span class="hljs-string">"error"</span>, text: <span class="hljs-string">"Something went wrong"</span> },
];
</code></pre>
<p><strong>Iterating Over Arrays:</strong><br>Arrays in TypeScript can be used in loops with full type safety:</p>
<pre><code class="lang-typescript">typescriptCopyEditconst scores: <span class="hljs-built_in">number</span>[] = [<span class="hljs-number">80</span>, <span class="hljs-number">90</span>, <span class="hljs-number">70</span>];
scores.forEach(<span class="hljs-function">(<span class="hljs-params">score</span>) =&gt;</span> <span class="hljs-built_in">console</span>.log(score + <span class="hljs-number">5</span>)); <span class="hljs-comment">// Adds 5 to each score</span>
</code></pre>
<p><strong>Function Parameters and Return Types:</strong><br>Arrays can also be passed as function parameters or returned by functions with strict typing:</p>
<pre><code class="lang-typescript">typescriptCopyEditfunction getNumbers(): <span class="hljs-built_in">number</span>[] {
  <span class="hljs-keyword">return</span> [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
}
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">printStrings</span>(<span class="hljs-params">strings: <span class="hljs-built_in">string</span>[]</span>): <span class="hljs-title">void</span> </span>{
  strings.forEach(<span class="hljs-function">(<span class="hljs-params">str</span>) =&gt;</span> <span class="hljs-built_in">console</span>.log(str));
}
</code></pre>
<h3 id="heading-function-types-in-typescript">Function Types in TypeScript</h3>
<p>Function types in TypeScript describe the shape of functions, including parameter types and return types. Function types are defined by explicitly specifying the parameter types during declaration. The return type is specified by adding <code>:</code> and the type to return immediately after the brackets. For Example:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addition</span> (<span class="hljs-params">a: <span class="hljs-built_in">number</span>, b: <span class="hljs-built_in">number</span></span>): <span class="hljs-title">number</span> </span>{
<span class="hljs-keyword">return</span> a + b;
}
</code></pre>
<p>The above function takes in two numbers, adds them, and returns a number. The function will not work if any of its arguments are not numbers and if it returns anything else except a number. For example:</p>
<ol>
<li>Calling the function with a string as the argument:</li>
</ol>
<pre><code class="lang-typescript"><span class="hljs-comment">// This won't work because it expects numbers, and one of the arguments is a string</span>

addition(<span class="hljs-number">1</span>, <span class="hljs-string">"two"</span>);
</code></pre>
<ol start="2">
<li>Re-writing the function to return a string:</li>
</ol>
<pre><code class="lang-typescript"><span class="hljs-comment">// Function will return an error because it's returning a string</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addition</span> (<span class="hljs-params">a: <span class="hljs-built_in">number</span>, b: <span class="hljs-built_in">number</span></span>): <span class="hljs-title">string</span> </span>{
    <span class="hljs-keyword">let</span> result = a + b;
    <span class="hljs-keyword">let</span> returnStatement = <span class="hljs-string">`Addition of <span class="hljs-subst">${a}</span> and <span class="hljs-subst">${b}</span> is: <span class="hljs-subst">${result}</span>`</span>;
    <span class="hljs-keyword">return</span> returnStatement;
}
</code></pre>
<p>Test the code out for yourself to see how these examples work.</p>
<p>Understanding and effectively handling objects, arrays, and functions in TypeScript empowers you to write type-safe and maintainable code, enhancing the reliability and scalability of your applications.</p>
<h2 id="heading-how-to-create-custom-types-in-typescript">How to Create Custom Types in TypeScript</h2>
<p>Often, your design pattern doesn't follow the built-in data types in TypeScript. For example, you might have patterns that use dynamic programming). And this can cause problems in your codebase. TypeScript offers a solution for creating <strong>custom types</strong> to address this issue.</p>
<p>Custom types allow you to define your data structure and shapes according to your needs. This enhances code readability and maintainability.</p>
<h3 id="heading-the-type-keyword">The Type Keyword</h3>
<p>The <code>type</code> keyword lets you create <strong>type aliases</strong>, providing a way to create custom types. The types you create can be reused throughout your codebase. Type aliases help define union types or combine types into single aliases. The syntax for creating a custom type is as follows:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Syntax</span>

<span class="hljs-keyword">type</span> TypeAlias = <span class="hljs-keyword">type</span>;
</code></pre>
<p>And here’s an example:</p>
<p><img src="https://i.ibb.co/qBZ3Zcw/Screenshot-2024-02-16-at-4-17-27-PM.png" alt="type Example" width="600" height="400" loading="lazy"></p>
<p>The code above creates a custom-type <code>UserName</code>, a union of numbers and strings. It uses the type created to define two variables relatively to check if the type works.</p>
<p>Note that it’s recommended to start a type alias starts with a capital letter.</p>
<p>The type Keyword is generally used for primitives – but how about creating a custom object type?</p>
<p>This is where <strong>Interfaces</strong> come in.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1737294121435/5be475e2-efae-428e-b9ed-bbcce7ce260d.jpeg" alt="5be475e2-efae-428e-b9ed-bbcce7ce260d" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-typescript-interfaces">TypeScript Interfaces</h3>
<p>Interfaces in TypeScript are used to define the structure of objects. They serve as blueprints, specifying the properties an object should have and their respective types. This ensures that objects conform to a consistent shape, enabling type safety and clearer code.</p>
<h4 id="heading-defining-an-interface">Defining an interface</h4>
<p>An interface is defined using the <code>interface</code> keyword. The syntax looks like this:</p>
<pre><code class="lang-typescript">typescriptCopyEditinterface InterfaceName {
  property1: Type;
  property2: Type;
}
</code></pre>
<h4 id="heading-example">Example:</h4>
<pre><code class="lang-typescript">typescriptCopyEditinterface User {
  id: <span class="hljs-built_in">number</span>;
  name: <span class="hljs-built_in">string</span>;
  email: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">const</span> user: User = {
  id: <span class="hljs-number">1</span>,
  name: <span class="hljs-string">"Alice"</span>,
  email: <span class="hljs-string">"alice@example.com"</span>,
};
</code></pre>
<p>Here’s what’s going on in this example:</p>
<ol>
<li><p><strong>Interface declaration (</strong><code>interface User</code>):</p>
<ul>
<li><p>Here, we define a blueprint for a <code>User</code> object. It specifies that any object of type <code>User</code> must have the following properties:</p>
<ul>
<li><p><code>id</code> of type <code>number</code></p>
</li>
<li><p><code>name</code> of type <code>string</code></p>
</li>
<li><p><code>email</code> of type <code>string</code></p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Using the interface (</strong><code>const user: User</code>):</p>
<ul>
<li><p>We declare an object <code>user</code> of type <code>User</code>.</p>
</li>
<li><p>The object is required to have all the properties defined in the <code>User</code> interface, with values of the specified types. If a property is missing or its type doesn't match, TypeScript will throw a compile-time error.</p>
</li>
</ul>
</li>
</ol>
<p>    For example:</p>
<pre><code class="lang-typescript">    typescriptCopyEditconst invalidUser: User = {
      id: <span class="hljs-number">1</span>,
      name: <span class="hljs-string">"Alice"</span>,
      <span class="hljs-comment">// Error: Property 'email' is missing in type</span>
    };
</code></pre>
<p>So you might be wondering – why should you use interfaces?</p>
<ul>
<li><p><strong>Type safety</strong>: Ensures that objects conform to the expected structure, preventing runtime errors.</p>
</li>
<li><p><strong>Reusability</strong>: The same interface can be reused across different parts of the application, reducing duplication.</p>
</li>
<li><p><strong>Code clarity</strong>: Makes the code easier to read and understand by explicitly describing the shape of objects.</p>
</li>
</ul>
<h4 id="heading-advanced-features-of-interfaces">Advanced Features of Interfaces</h4>
<ol>
<li><p><strong>Optional properties</strong>: You can make properties optional by adding a question mark (<code>?</code>).</p>
<pre><code class="lang-typescript"> typescriptCopyEditinterface Product {
   id: <span class="hljs-built_in">number</span>;
   name: <span class="hljs-built_in">string</span>;
   description?: <span class="hljs-built_in">string</span>; <span class="hljs-comment">// Optional property</span>
 }

 <span class="hljs-keyword">const</span> product: Product = {
   id: <span class="hljs-number">101</span>,
   name: <span class="hljs-string">"Laptop"</span>,
 }; <span class="hljs-comment">// Valid, as 'description' is optional</span>
</code></pre>
</li>
<li><p><strong>Readonly properties</strong>: Use <code>readonly</code> to prevent properties from being modified after initialization.</p>
<pre><code class="lang-typescript"> typescriptCopyEditinterface Point {
   <span class="hljs-keyword">readonly</span> x: <span class="hljs-built_in">number</span>;
   <span class="hljs-keyword">readonly</span> y: <span class="hljs-built_in">number</span>;
 }

 <span class="hljs-keyword">const</span> point: Point = { x: <span class="hljs-number">10</span>, y: <span class="hljs-number">20</span> };
 point.x = <span class="hljs-number">15</span>; <span class="hljs-comment">// Error: Cannot assign to 'x' because it is a read-only property</span>
</code></pre>
</li>
<li><p><strong>Extending interfaces</strong>: Interfaces can inherit properties from other interfaces, enabling composition.</p>
<pre><code class="lang-typescript"> typescriptCopyEditinterface Person {
   name: <span class="hljs-built_in">string</span>;
   age: <span class="hljs-built_in">number</span>;
 }

 <span class="hljs-keyword">interface</span> Employee <span class="hljs-keyword">extends</span> Person {
   employeeId: <span class="hljs-built_in">number</span>;
 }

 <span class="hljs-keyword">const</span> employee: Employee = {
   name: <span class="hljs-string">"John"</span>,
   age: <span class="hljs-number">30</span>,
   employeeId: <span class="hljs-number">1234</span>,
 };
</code></pre>
</li>
</ol>
<h3 id="heading-when-to-use-interfaces"><strong>When to Use Interfaces</strong></h3>
<p>There are various scenarios when it’s a good idea to use interfaces. You can use them when you want to define and enforce the structure of objects passed around in your code.</p>
<p>They’re also useful in API responses, as they help you type-check objects received from APIs. This ensures that the data conforms to your expectations.</p>
<p>Interfaces are also handy when working with reusable types. When multiple parts of your application use objects with the same structure, interfaces prevent duplication.</p>
<p>By leveraging interfaces, you can create robust, maintainable, and type-safe applications. They are an essential feature of TypeScript that promotes clean and predictable code.</p>
<h3 id="heading-generics-and-literal-types">Generics and Literal Types</h3>
<p><strong>Generics</strong> in TypeScript allow you to create reusable components that can work with various data types. They let you write functions, classes, and interfaces without specifying the exact type upfront, making your code more flexible and maintainable.</p>
<p>Here's an example of a generic function and a generic interface in TypeScript:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Generic interface for a box that can hold any value </span>

<span class="hljs-keyword">interface</span>  Box&lt;T&gt; { 
    value: T; 
}

<span class="hljs-comment">// Usage examples</span>

<span class="hljs-keyword">let</span>  numberBox: Box&lt;<span class="hljs-built_in">number</span>&gt; = { value: <span class="hljs-number">10</span> };
<span class="hljs-keyword">let</span>  stringBox: Box&lt;<span class="hljs-built_in">string</span>&gt; = { value: <span class="hljs-string">"TypeScript"</span> };

<span class="hljs-built_in">console</span>.log(numberBox.value); <span class="hljs-comment">// Output: 10  </span>
<span class="hljs-built_in">console</span>.log(stringBox.value); <span class="hljs-comment">// Output: TypeScript</span>
</code></pre>
<p>You can use generics when you’re unsure of your data type.</p>
<p>In contrast to Generics, <strong>Literal types</strong> allow you to specify exact values a variable can hold. This adds increased specificity and type safety to your code, preventing unintended values from being assigned. Here’s an example:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> Direction = <span class="hljs-string">'up'</span> | <span class="hljs-string">'down'</span> | <span class="hljs-string">'left'</span> | <span class="hljs-string">'right'</span>;
</code></pre>
<p>A variable created with the above type can only be assigned for the strings up, down, left, and right.</p>
<p>Overall, leveraging custom types in TypeScript empowers you to create expressive, reusable, and type-safe data structures, helping you develop more robust and maintainable applications.</p>
<h2 id="heading-how-to-merge-types-in-typescript">How to Merge Types in TypeScript</h2>
<p>Merging types in TypeScript combines multiple type declarations into a single, unified type. This capability allows developers to build complex types from smaller, reusable pieces, enhancing code clarity, reusability, and maintainability.</p>
<h3 id="heading-1-declaration-merging-in-interfaces"><strong>1. Declaration Merging in Interfaces</strong></h3>
<p>TypeScript supports <strong>declaration merging</strong>, where multiple interface declarations with the same name are automatically combined into a single interface. This lets you augment an existing interface by defining additional properties or methods.</p>
<h5 id="heading-example-1"><strong>Example:</strong></h5>
<pre><code class="lang-typescript">typescriptCopyEditinterface User {
  id: <span class="hljs-built_in">number</span>;
  name: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">interface</span> User {
  email: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">const</span> user: User = {
  id: <span class="hljs-number">1</span>,
  name: <span class="hljs-string">"Alice"</span>,
  email: <span class="hljs-string">"alice@example.com"</span>,
};
</code></pre>
<h5 id="heading-how-it-works"><strong>How it works:</strong></h5>
<ul>
<li><p>The <code>User</code> interface is declared twice, each with different properties.</p>
</li>
<li><p>TypeScript automatically merges these declarations into a single interface:</p>
<pre><code class="lang-typescript">  typescriptCopyEditinterface User {
    id: <span class="hljs-built_in">number</span>;
    name: <span class="hljs-built_in">string</span>;
    email: <span class="hljs-built_in">string</span>;
  }
</code></pre>
</li>
<li><p>When creating the <code>user</code> object, all properties from the merged interface must be present. If any property is missing, TypeScript will raise an error.</p>
</li>
</ul>
<p>Declaration merging is particularly useful when working with third-party libraries. You can extend or add new properties to an existing interface without modifying the library's source code.</p>
<h3 id="heading-2-interface-merging-using-the-extends-keyword"><strong>2. Interface Merging Using the</strong> <code>extends</code> Keyword</h3>
<p>The <code>extends</code> keyword allows one interface to inherit properties and methods from another, creating a new interface that combines the properties of both.</p>
<h5 id="heading-example-2"><strong>Example:</strong></h5>
<pre><code class="lang-typescript">typescriptCopyEditinterface Person {
  name: <span class="hljs-built_in">string</span>;
  age: <span class="hljs-built_in">number</span>;
}

<span class="hljs-keyword">interface</span> Employee <span class="hljs-keyword">extends</span> Person {
  employeeId: <span class="hljs-built_in">number</span>;
}

<span class="hljs-keyword">const</span> employee: Employee = {
  name: <span class="hljs-string">"John"</span>,
  age: <span class="hljs-number">30</span>,
  employeeId: <span class="hljs-number">101</span>,
};
</code></pre>
<h5 id="heading-how-it-works-1"><strong>How it works:</strong></h5>
<ul>
<li><p>The <code>Person</code> interface defines two properties: <code>name</code> and <code>age</code>.</p>
</li>
<li><p>The <code>Employee</code> interface uses the <code>extends</code> keyword to inherit the properties from <code>Person</code>.</p>
</li>
<li><p>The <code>Employee</code> interface also adds a new property, <code>employeeId</code>.</p>
</li>
<li><p>The <code>employee</code> object must include all properties from both <code>Person</code> and <code>Employee</code>.</p>
</li>
</ul>
<h5 id="heading-this-approach-is-ideal-for-hierarchical-relationships-for-instance-you-can-define-a-base-interface-for-shared-properties-and-extend-it-for-specialized-types">This approach is ideal for hierarchical relationships. For instance, you can define a base interface for shared properties and extend it for specialized types.</h5>
<h3 id="heading-3-type-merging-using-the-amp-operator"><strong>3. Type Merging Using the</strong> <code>&amp;</code> Operator</h3>
<p>The <code>&amp;</code> operator, known as the intersection type, allows you to combine multiple types into a single type. The resulting type includes all properties and methods from each type.</p>
<h5 id="heading-example-3"><strong>Example:</strong></h5>
<pre><code class="lang-typescript">typescriptCopyEdittype Address = {
  city: <span class="hljs-built_in">string</span>;
  country: <span class="hljs-built_in">string</span>;
};

<span class="hljs-keyword">type</span> ContactInfo = {
  email: <span class="hljs-built_in">string</span>;
  phone: <span class="hljs-built_in">string</span>;
};

<span class="hljs-keyword">type</span> EmployeeDetails = Address &amp; ContactInfo;

<span class="hljs-keyword">const</span> employee: EmployeeDetails = {
  city: <span class="hljs-string">"New York"</span>,
  country: <span class="hljs-string">"USA"</span>,
  email: <span class="hljs-string">"john.doe@example.com"</span>,
  phone: <span class="hljs-string">"123-456-7890"</span>,
};
</code></pre>
<h5 id="heading-how-it-works-2"><strong>How it works:</strong></h5>
<ul>
<li><p><code>Address</code> and <code>ContactInfo</code> are two separate types.</p>
</li>
<li><p><code>EmployeeDetails</code> is an intersection type created using <code>Address &amp; ContactInfo</code>.</p>
</li>
<li><p>The <code>employee</code> object must include all properties from both <code>Address</code> and <code>ContactInfo</code>. Missing or incorrectly typed properties will result in a TypeScript error.</p>
</li>
</ul>
<h5 id="heading-intersection-types-are-helpful-when-you-need-to-combine-unrelated-types-or-create-composite-types-for-specific-use-cases-like-api-responses-that-merge-different-data-structures">Intersection types are helpful when you need to combine unrelated types or create composite types for specific use cases, like API responses that merge different data structures.</h5>
<h3 id="heading-when-to-use-each-of-these-approaches"><strong>When to Use Each of These Approaches</strong></h3>
<ol>
<li><p><strong>Declaration merging:</strong> Use when you want to extend or augment an existing interface, particularly in third-party libraries or shared codebases.</p>
</li>
<li><p><code>extends</code> <strong>keyword</strong>: Use for hierarchical relationships where a base interface can be specialized into more specific types.</p>
</li>
<li><p><strong>Intersection types (</strong><code>&amp;</code>): Use when you need to combine multiple unrelated types into a single type for specific use cases.</p>
</li>
</ol>
<p>By understanding these merging techniques and their implications, you can structure your TypeScript code effectively, improving reusability and maintainability while maintaining type safety.</p>
<h2 id="heading-bundling-and-transformations-in-typescript">Bundling and Transformations in TypeScript</h2>
<p>Not every browser supports the latest JavaScript used by TypeScript. So you can use the <strong>TypeScript compiler</strong>, or <code>tsc</code>, to convert TypeScript code (.ts files) into conventional JavaScript (.js files) that’s universally compatible with all browsers. <code>tsc</code> translates TypeScript-specific elements like types and classes into JavaScript code that browsers can interpret.</p>
<p>To execute TypeScript files, <code>tsc</code> is your go-to. You can install <code>tsc</code> using npm and then transform your .ts files into .js files. To use <code>tsc</code>, just specify the name of the TypeScript file before the <code>tsc</code> command. For instance, if you have a file named <code>app.ts</code>, you can run it by typing:</p>
<pre><code class="lang-bash">tsc app.ts
</code></pre>
<p>Webpack or Parcel are frequently employed to deploy TypeScript code on browsers. These tools bundle all JavaScript files, including those from TypeScript, for improved performance and easier website implementation. They also optimize code loading by reducing its size and enhancing browser speed.</p>
<h2 id="heading-building-better-code-with-typescript">Building Better Code with TypeScript</h2>
<p>Embracing TypeScript as a JavaScript developer opens up possibilities for writing more robust and maintainable code. By understanding the basics and core concepts outlined in this guide, you can leverage TypeScript's static typing system to catch errors early in development, leading to fewer bugs and smoother code maintenance.</p>
<p>By using TypeScript, JavaScript devs can enhance their code quality and productivity. As you continue to explore and practice with TypeScript, you will discover even more powerful features and functionalities.</p>
<p>Keep pushing your boundaries and dive deeper into the world of TypeScript. 😉</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Handle Complex Use Cases in Your OpenAPI Specifications – API Documentation Guide ]]>
                </title>
                <description>
                    <![CDATA[ When you’re documenting an API reference, there are two main approaches you can follow. You can either use the manual approach of filling in the endpoints via a user interface, or organize a structured document containing all the necessary informatio... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-handle-complex-use-cases-in-api-specs/</link>
                <guid isPermaLink="false">6728f9e02b7cd4e7135f2f39</guid>
                
                    <category>
                        <![CDATA[ Technical writing  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ OpenApi ]]>
                    </category>
                
                    <category>
                        <![CDATA[ APIs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ documentation ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Onyeanuna Prince ]]>
                </dc:creator>
                <pubDate>Mon, 04 Nov 2024 16:44:16 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1730415311732/58afe01c-0ac4-4351-a4b4-a15729b5bcb1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When you’re documenting an API reference, there are two main approaches you can follow. You can either use the manual approach of filling in the endpoints via a user interface, or organize a structured document containing all the necessary information about your API.</p>
<p>This structured document is called an <a target="_blank" href="https://www.openapis.org/">OpenAPI specification</a> (OpenAPI Spec or OAS).</p>
<p>An OpenAPI spec is a format for describing APIs. It's a blueprint that outlines everything about how an API works — what endpoints are available, what data you can send or receive, and what responses to expect.</p>
<p>This means that a well-written OAS file means well-written API reference documentation.</p>
<p>When writing this file, there are some parts that can get a bit complicated. For example, you might have to document a single endpoint with different methods or sometimes duplicate endpoints.</p>
<p>I encountered and documented both of those use cases. So, in this article, I'll show you how you can do the same. We'll go through each use case with sample OpenAPI specs, and at the end, I'll leave you with some useful tips for documenting your OpenAPI spec files.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></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-setting-the-foundation">Setting the Foundation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-case-1-duplicate-endpoints">Use Case 1: Duplicate Endpoints</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-case-2-how-to-document-multiple-http-methods">Use Case 2: How to Document Multiple HTTP Methods</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-api-documentation-tips">API Documentation Tips</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-use-markdown-in-openapi">Use Markdown in OpenAPI</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-the-operationid-field">Use the operationId Field</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-ref-for-reusable-components">Use $ref for Reusable Components</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>There are a few things you need to know to follow along with the use cases below. These include:</p>
<ol>
<li><p><a target="_blank" href="https://www.freecodecamp.org/news/how-apis-work/"><strong>A basic knowledge of APIs and API documentation</strong></a>: Familiarity with API terminology and structure (for example, endpoints, methods, request/response structure) is essential to understand how an OpenAPI Specification (OAS) document functions.</p>
</li>
<li><p><a target="_blank" href="https://spec.openapis.org/oas/latest.html"><strong>Familiarity with OpenAPI Specification</strong></a>: Basic understanding of OAS, including its purpose, structure, and so on.</p>
</li>
<li><p><strong>Access to Swagger or other OpenAPI documentation tools</strong>: You need tools like the <a target="_blank" href="https://editor-next.swagger.io/">Swagger Editor</a> or <a target="_blank" href="https://rapidocweb.com/">RapiDoc</a>, which will allow you to test and view the OpenAPI files visually.</p>
</li>
</ol>
<h2 id="heading-setting-the-foundation"><strong>Setting the Foundation</strong></h2>
<p>In both programming and in life, if you can determine that something is "complex," this means that there's also a simplistic-regular way it can be as well. And that's the same for an OAS file.</p>
<p>When creating your spec file, you might not always encounter the use cases we'll address shortly. That's why it’s useful to know what a conventional spec file looks like.</p>
<p>An OpenAPI spec is a human and machine-readable file written in JSON or YAML. Below is an example of an OAS file structure in YAML format:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">paths:</span>
  <span class="hljs-string">/users:</span>
    <span class="hljs-attr">get:</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">Get</span> <span class="hljs-string">a</span> <span class="hljs-string">list</span> <span class="hljs-string">of</span> <span class="hljs-string">users</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'200':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">A</span> <span class="hljs-string">list</span> <span class="hljs-string">of</span> <span class="hljs-string">users.</span>
  <span class="hljs-string">/users/{userId}:</span>
    <span class="hljs-attr">get:</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">Get</span> <span class="hljs-string">details</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">single</span> <span class="hljs-string">user</span>
      <span class="hljs-attr">parameters:</span>
 <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">userId</span>
          <span class="hljs-attr">in:</span> <span class="hljs-string">path</span>
          <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>
          <span class="hljs-attr">schema:</span>
            <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'200':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Details</span> <span class="hljs-string">of</span> <span class="hljs-string">a</span> <span class="hljs-string">single</span> <span class="hljs-string">user.</span>
  <span class="hljs-string">/orders:</span>
    <span class="hljs-attr">get:</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">Get</span> <span class="hljs-string">a</span> <span class="hljs-string">list</span> <span class="hljs-string">of</span> <span class="hljs-string">orders</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'200':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">A</span> <span class="hljs-string">list</span> <span class="hljs-string">of</span> <span class="hljs-string">orders.</span>
</code></pre>
<p>Here's a brief breakdown of this OAS file structure:</p>
<ol>
<li><p><strong>Paths</strong>: The <code>paths</code> section allows you to list each endpoint or URL that you can interact with in this API. Each path, such as <code>/users</code> or <code>/orders</code>, shows what type of data you can get or send to that URL.</p>
</li>
<li><p><strong>Operations</strong>: Under each path, there's a method or operation, like <code>GET</code>, which tells us what we can do with that endpoint. In this example, each path uses <code>GET</code>, which means you request information.</p>
</li>
<li><p><strong>Summary</strong>: Each operation has a <code>summary</code> — a short description explaining what the endpoint does, like "Get a list of users" or "Get details for a single user."</p>
</li>
<li><p><strong>Parameters</strong>: For paths that include placeholders, like <code>/users/{userId}</code>, you'll see a <code>parameters</code> section explaining what details are required.</p>
</li>
<li><p><strong>Responses</strong>: Each operation includes a <code>responses</code> section, which lists the possible API responses.</p>
</li>
</ol>
<p><strong>NOTE</strong>: This is not a complete OAS file. I omitted some preceding information for the purpose of this explanation.</p>
<p>Now, let’s dive into the more complex scenarios.</p>
<h2 id="heading-use-case-1-duplicate-endpoints"><strong>Use Case 1: Duplicate Endpoints</strong></h2>
<p>During the development of an API, you may create a single endpoint with multiple variations. Depending on the use case, you might want that endpoint to accept multiple data formats or specific parameters.</p>
<p>When you encounter such scenarios and want to document the API reference using an OpenAPI spec, you won't be able to replicate it exactly how it is in the <a target="_blank" href="https://postman.com/">Postman</a> collection (or any other development environment).</p>
<p>If you did try, you'll get this error:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730414590959/017c94c1-7e33-46c7-b572-b9ea03763e1f.png" alt="Failed OpenAPI Spec" width="2940" height="1490" loading="lazy"></p>
<p>The workaround for this problem is to consolidate these multiple variations under a single path definition by grouping the different requests and responses as <strong>examples</strong>.</p>
<p>In the example below, you have an API that has an endpoint for managing session registration at a conference. This endpoint has various requests and responses based on registration type (for example, Speaker, Attendee, or VIP).</p>
<p>In a Postman collection, you can have each of these endpoints in a separate folder for easy identification and testing.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730414608239/be94b45d-fb16-438f-9e7e-07cdc38c179d.png" alt="Postman collection" width="2938" height="792" loading="lazy"></p>
<p>But when documenting your spec file, it should look like this:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">openapi:</span> <span class="hljs-number">3.0</span><span class="hljs-number">.0</span>
<span class="hljs-attr">info:</span>
  <span class="hljs-attr">title:</span> <span class="hljs-string">Conference</span> <span class="hljs-string">Events</span> <span class="hljs-string">API</span>
  <span class="hljs-attr">description:</span> <span class="hljs-string">API</span> <span class="hljs-string">to</span> <span class="hljs-string">manage</span> <span class="hljs-string">event</span> <span class="hljs-string">registrations</span> <span class="hljs-string">for</span> <span class="hljs-string">conferences</span>
  <span class="hljs-attr">version:</span> <span class="hljs-number">1.0</span><span class="hljs-number">.0</span>
<span class="hljs-attr">paths:</span>
  <span class="hljs-string">/register-session:</span>
    <span class="hljs-attr">post:</span>
      <span class="hljs-attr">tags:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">Registration</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">Register</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">conference</span> <span class="hljs-string">session</span>
      <span class="hljs-attr">description:</span> <span class="hljs-string">Register</span> <span class="hljs-string">a</span> <span class="hljs-string">user</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">specific</span> <span class="hljs-string">session</span> <span class="hljs-string">based</span> <span class="hljs-string">on</span> <span class="hljs-string">their</span> <span class="hljs-string">role,</span> <span class="hljs-string">with</span> <span class="hljs-string">details</span> <span class="hljs-string">provided</span> <span class="hljs-string">for</span> <span class="hljs-string">each</span> <span class="hljs-string">registration</span> <span class="hljs-string">type.</span>
      <span class="hljs-attr">operationId:</span> <span class="hljs-string">registerSession</span>
      <span class="hljs-attr">requestBody:</span>
        <span class="hljs-attr">description:</span> <span class="hljs-string">Registers</span> <span class="hljs-string">a</span> <span class="hljs-string">user</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">session</span> <span class="hljs-string">at</span> <span class="hljs-string">the</span> <span class="hljs-string">conference.</span> <span class="hljs-string">It</span> <span class="hljs-string">accepts</span> <span class="hljs-string">different</span> <span class="hljs-string">formats</span> <span class="hljs-string">for</span> <span class="hljs-string">attendance,</span> <span class="hljs-string">speaker,</span> <span class="hljs-string">or</span> <span class="hljs-string">VIP</span> <span class="hljs-string">registrations.</span>
        <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>
        <span class="hljs-attr">content:</span>
          <span class="hljs-attr">application/json:</span>
            <span class="hljs-attr">schema:</span>
              <span class="hljs-string">$ref:</span> <span class="hljs-string">'#/components/schemas/SessionRegistration'</span>
            <span class="hljs-attr">examples:</span>
              <span class="hljs-attr">Attendee:</span>
                <span class="hljs-attr">summary:</span> <span class="hljs-string">Register</span> <span class="hljs-string">as</span> <span class="hljs-string">an</span> <span class="hljs-string">Attendee</span>
                <span class="hljs-attr">value:</span>
                  <span class="hljs-attr">userType:</span> <span class="hljs-string">Attendee</span>
                  <span class="hljs-attr">userId:</span> <span class="hljs-number">789</span>
                  <span class="hljs-attr">sessionId:</span> <span class="hljs-number">1234</span>
                  <span class="hljs-attr">preferences:</span>
                    <span class="hljs-attr">seating:</span> <span class="hljs-string">General</span>
                    <span class="hljs-attr">accessLevel:</span> <span class="hljs-string">Basic</span>
              <span class="hljs-attr">Speaker:</span>
                <span class="hljs-attr">summary:</span> <span class="hljs-string">Register</span> <span class="hljs-string">as</span> <span class="hljs-string">a</span> <span class="hljs-string">Speaker</span>
                <span class="hljs-attr">value:</span>
                  <span class="hljs-attr">userType:</span> <span class="hljs-string">Speaker</span>
                  <span class="hljs-attr">userId:</span> <span class="hljs-number">456</span>
                  <span class="hljs-attr">sessionId:</span> <span class="hljs-number">1234</span>
                  <span class="hljs-attr">preferences:</span>
                    <span class="hljs-attr">seating:</span> <span class="hljs-string">VIP</span>
                    <span class="hljs-attr">accessLevel:</span> <span class="hljs-string">Full</span>
                    <span class="hljs-attr">presentationEquipment:</span> <span class="hljs-string">Projector</span>
              <span class="hljs-attr">VIP:</span>
                <span class="hljs-attr">summary:</span> <span class="hljs-string">Register</span> <span class="hljs-string">as</span> <span class="hljs-string">VIP</span>
                <span class="hljs-attr">value:</span>
                  <span class="hljs-attr">userType:</span> <span class="hljs-string">VIP</span>
                  <span class="hljs-attr">userId:</span> <span class="hljs-number">123</span>
                  <span class="hljs-attr">sessionId:</span> <span class="hljs-number">1234</span>
                  <span class="hljs-attr">preferences:</span>
                    <span class="hljs-attr">seating:</span> <span class="hljs-string">Front</span> <span class="hljs-string">Row</span>
                    <span class="hljs-attr">accessLevel:</span> <span class="hljs-string">Full</span>
                    <span class="hljs-attr">exclusiveAccess:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'200':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Successful</span> <span class="hljs-string">registration</span> <span class="hljs-string">for</span> <span class="hljs-string">Attendee,</span> <span class="hljs-string">Speaker,</span> <span class="hljs-string">or</span> <span class="hljs-string">VIP</span>
          <span class="hljs-attr">content:</span>
            <span class="hljs-attr">application/json:</span>
              <span class="hljs-attr">schema:</span>
                <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
                <span class="hljs-attr">properties:</span>
                  <span class="hljs-attr">registrationId:</span>
                    <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
                  <span class="hljs-attr">success:</span>
                    <span class="hljs-attr">type:</span> <span class="hljs-string">boolean</span>
              <span class="hljs-attr">examples:</span>
                <span class="hljs-attr">Attendee:</span>
                  <span class="hljs-attr">summary:</span> <span class="hljs-string">Response</span> <span class="hljs-string">for</span> <span class="hljs-string">Attendee</span> <span class="hljs-string">registration</span>
                  <span class="hljs-attr">value:</span>
                    <span class="hljs-attr">registrationId:</span> <span class="hljs-string">att-456def</span>
                    <span class="hljs-attr">success:</span> <span class="hljs-literal">true</span>
                <span class="hljs-attr">Speaker:</span>
                  <span class="hljs-attr">summary:</span> <span class="hljs-string">Response</span> <span class="hljs-string">for</span> <span class="hljs-string">Speaker</span> <span class="hljs-string">registration</span>
                  <span class="hljs-attr">value:</span>
                    <span class="hljs-attr">registrationId:</span> <span class="hljs-string">spk-123abc</span>
                    <span class="hljs-attr">success:</span> <span class="hljs-literal">true</span>
                <span class="hljs-attr">VIP:</span>
                  <span class="hljs-attr">summary:</span> <span class="hljs-string">Response</span> <span class="hljs-string">for</span> <span class="hljs-string">VIP</span> <span class="hljs-string">registration</span>
                  <span class="hljs-attr">value:</span>
                    <span class="hljs-attr">registrationId:</span> <span class="hljs-string">vip-789ghi</span>
                    <span class="hljs-attr">success:</span> <span class="hljs-literal">true</span>
<span class="hljs-attr">components:</span>
  <span class="hljs-attr">schemas:</span>
    <span class="hljs-attr">SessionRegistration:</span>
      <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
      <span class="hljs-attr">properties:</span>
        <span class="hljs-attr">userType:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Type</span> <span class="hljs-string">of</span> <span class="hljs-string">user</span> <span class="hljs-string">registering</span> <span class="hljs-string">(e.g.,</span> <span class="hljs-string">Attendee,</span> <span class="hljs-string">Speaker,</span> <span class="hljs-string">VIP)</span>
        <span class="hljs-attr">userId:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">integer</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Unique</span> <span class="hljs-string">ID</span> <span class="hljs-string">of</span> <span class="hljs-string">the</span> <span class="hljs-string">user</span>
        <span class="hljs-attr">sessionId:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">integer</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Unique</span> <span class="hljs-string">ID</span> <span class="hljs-string">of</span> <span class="hljs-string">the</span> <span class="hljs-string">session</span> <span class="hljs-string">to</span> <span class="hljs-string">register</span> <span class="hljs-string">for</span>
        <span class="hljs-attr">preferences:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
          <span class="hljs-attr">properties:</span>
            <span class="hljs-attr">seating:</span>
              <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
              <span class="hljs-attr">description:</span> <span class="hljs-string">Seating</span> <span class="hljs-string">preference</span> <span class="hljs-string">(e.g.,</span> <span class="hljs-string">General,</span> <span class="hljs-string">VIP,</span> <span class="hljs-string">Front</span> <span class="hljs-string">Row)</span>
            <span class="hljs-attr">accessLevel:</span>
              <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
              <span class="hljs-attr">description:</span> <span class="hljs-string">Access</span> <span class="hljs-string">level</span> <span class="hljs-string">granted</span> <span class="hljs-string">(e.g.,</span> <span class="hljs-string">Basic,</span> <span class="hljs-string">Full)</span>
            <span class="hljs-attr">presentationEquipment:</span>
              <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
              <span class="hljs-attr">description:</span> <span class="hljs-string">Required</span> <span class="hljs-string">equipment</span> <span class="hljs-string">for</span> <span class="hljs-string">Speakers</span> <span class="hljs-string">(only</span> <span class="hljs-string">applicable</span> <span class="hljs-string">to</span> <span class="hljs-string">Speaker)</span>
            <span class="hljs-attr">exclusiveAccess:</span>
              <span class="hljs-attr">type:</span> <span class="hljs-string">boolean</span>
              <span class="hljs-attr">description:</span> <span class="hljs-string">Exclusive</span> <span class="hljs-string">access</span> <span class="hljs-string">for</span> <span class="hljs-string">VIP</span> <span class="hljs-string">users</span>
</code></pre>
<p>In this file, you have a single <code>POST /register-session</code> path that captures all registration types without duplicating endpoints.</p>
<p>Here's a visual representation of this OAS file using the <a target="_blank" href="https://editor-next.swagger.io/">Swagger editor</a>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730414627353/eed5843b-f5f4-4e61-9126-51fd10e417c6.png" alt="OpenAPI Spec Multiple Request Examples" width="2940" height="1598" loading="lazy"></p>
<h2 id="heading-use-case-2-how-to-document-multiple-http-methods"><strong>Use Case 2: How to Document Multiple HTTP Methods</strong></h2>
<p>Another use case you may encounter happens when you have the same endpoint but different HTTP methods.</p>
<p>This usually comes up because each method serves a different purpose, even though they share the same path.</p>
<p>For example, a <strong>GET</strong> method retrieves information about a resource, like viewing a user's registration details for a conference session.</p>
<p>On the other hand, a <strong>PATCH</strong> method updates specific fields for that same user registration, like updating their seat preference.</p>
<p>Since both the <code>GET</code> and <code>PATCH</code> methods relate to the same resource (<code>/register-session</code> in our example), the way around this is to group them under the same path. This way, you'll be documenting two separate methods for a single path.</p>
<p>In OpenAPI, each combination of a path and method is called an "operation". Grouping operations that share the same path helps maintain clearer and more structured documents.</p>
<p>Using the conference events API example, this is what your OAS file should look like:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">openapi:</span> <span class="hljs-number">3.0</span><span class="hljs-number">.0</span>
<span class="hljs-attr">info:</span>
  <span class="hljs-attr">title:</span> <span class="hljs-string">Conference</span> <span class="hljs-string">Events</span> <span class="hljs-string">API</span>
  <span class="hljs-attr">description:</span> <span class="hljs-string">API</span> <span class="hljs-string">to</span> <span class="hljs-string">manage</span> <span class="hljs-string">event</span> <span class="hljs-string">registrations</span> <span class="hljs-string">for</span> <span class="hljs-string">conferences</span>
  <span class="hljs-attr">version:</span> <span class="hljs-number">1.0</span><span class="hljs-number">.0</span>
<span class="hljs-attr">paths:</span>
  <span class="hljs-string">/register-session:</span>
    <span class="hljs-attr">get:</span>
      <span class="hljs-attr">tags:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">Registration</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">Retrieve</span> <span class="hljs-string">a</span> <span class="hljs-string">registration</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">conference</span> <span class="hljs-string">session</span>
      <span class="hljs-attr">description:</span> <span class="hljs-string">Fetch</span> <span class="hljs-string">details</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">specific</span> <span class="hljs-string">user's</span> <span class="hljs-string">registration,</span> <span class="hljs-string">like</span> <span class="hljs-string">seat</span> <span class="hljs-string">assignment</span> <span class="hljs-string">and</span> <span class="hljs-string">access</span> <span class="hljs-string">level.</span>
      <span class="hljs-attr">operationId:</span> <span class="hljs-string">getSessionRegistration</span>
      <span class="hljs-attr">parameters:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">in:</span> <span class="hljs-string">query</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">userId</span>
          <span class="hljs-attr">schema:</span>
            <span class="hljs-attr">type:</span> <span class="hljs-string">integer</span>
          <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">The</span> <span class="hljs-string">ID</span> <span class="hljs-string">of</span> <span class="hljs-string">the</span> <span class="hljs-string">user</span> <span class="hljs-string">whose</span> <span class="hljs-string">registration</span> <span class="hljs-string">you</span> <span class="hljs-string">want</span> <span class="hljs-string">to</span> <span class="hljs-string">retrieve.</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'200':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Registration</span> <span class="hljs-string">details</span> <span class="hljs-string">retrieved</span> <span class="hljs-string">successfully</span>
          <span class="hljs-attr">content:</span>
            <span class="hljs-attr">application/json:</span>
              <span class="hljs-attr">schema:</span>
                <span class="hljs-string">$ref:</span> <span class="hljs-string">'#/components/schemas/SessionRegistration'</span>
              <span class="hljs-attr">example:</span>
                <span class="hljs-attr">userId:</span> <span class="hljs-number">789</span>
                <span class="hljs-attr">sessionId:</span> <span class="hljs-number">1234</span>
                <span class="hljs-attr">preferences:</span>
                  <span class="hljs-attr">seating:</span> <span class="hljs-string">General</span>
                  <span class="hljs-attr">accessLevel:</span> <span class="hljs-string">Basic</span>
                  <span class="hljs-attr">exclusiveAccess:</span> <span class="hljs-literal">false</span>

    <span class="hljs-attr">patch:</span>
      <span class="hljs-attr">tags:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">Registration</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">Update</span> <span class="hljs-string">a</span> <span class="hljs-string">registration</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">conference</span> <span class="hljs-string">session</span>
      <span class="hljs-attr">description:</span> <span class="hljs-string">Update</span> <span class="hljs-string">specific</span> <span class="hljs-string">fields</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">user's</span> <span class="hljs-string">session</span> <span class="hljs-string">registration,</span> <span class="hljs-string">such</span> <span class="hljs-string">as</span> <span class="hljs-string">seating</span> <span class="hljs-string">or</span> <span class="hljs-string">access</span> <span class="hljs-string">level.</span>
      <span class="hljs-attr">operationId:</span> <span class="hljs-string">updateSessionRegistration</span>
      <span class="hljs-attr">requestBody:</span>
        <span class="hljs-attr">description:</span> <span class="hljs-string">Allows</span> <span class="hljs-string">updating</span> <span class="hljs-string">fields</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">specific</span> <span class="hljs-string">session</span> <span class="hljs-string">registration.</span>
        <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>
        <span class="hljs-attr">content:</span>
          <span class="hljs-attr">application/json:</span>
            <span class="hljs-attr">schema:</span>
              <span class="hljs-string">$ref:</span> <span class="hljs-string">'#/components/schemas/UpdateSessionPreferences'</span>
            <span class="hljs-attr">example:</span>
              <span class="hljs-attr">userId:</span> <span class="hljs-number">789</span>
              <span class="hljs-attr">preferences:</span>
                <span class="hljs-attr">seating:</span> <span class="hljs-string">VIP</span>
                <span class="hljs-attr">accessLevel:</span> <span class="hljs-string">Full</span>
                <span class="hljs-attr">exclusiveAccess:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'200':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Registration</span> <span class="hljs-string">updated</span> <span class="hljs-string">successfully</span>
          <span class="hljs-attr">content:</span>
            <span class="hljs-attr">application/json:</span>
              <span class="hljs-attr">schema:</span>
                <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
                <span class="hljs-attr">properties:</span>
                  <span class="hljs-attr">success:</span>
                    <span class="hljs-attr">type:</span> <span class="hljs-string">boolean</span>
                  <span class="hljs-attr">message:</span>
                    <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
              <span class="hljs-attr">example:</span>
                <span class="hljs-attr">success:</span> <span class="hljs-literal">true</span>
                <span class="hljs-attr">message:</span> <span class="hljs-string">"Registration updated successfully."</span>
<span class="hljs-attr">components:</span>
  <span class="hljs-attr">schemas:</span>
    <span class="hljs-attr">SessionRegistration:</span>
      <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
      <span class="hljs-attr">properties:</span>
        <span class="hljs-attr">userId:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">integer</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Unique</span> <span class="hljs-string">ID</span> <span class="hljs-string">of</span> <span class="hljs-string">the</span> <span class="hljs-string">user</span>
        <span class="hljs-attr">sessionId:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">integer</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Unique</span> <span class="hljs-string">ID</span> <span class="hljs-string">of</span> <span class="hljs-string">the</span> <span class="hljs-string">session</span>
        <span class="hljs-attr">preferences:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
          <span class="hljs-attr">properties:</span>
            <span class="hljs-attr">seating:</span>
              <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
              <span class="hljs-attr">description:</span> <span class="hljs-string">Seating</span> <span class="hljs-string">preference</span> <span class="hljs-string">(e.g.,</span> <span class="hljs-string">General,</span> <span class="hljs-string">VIP,</span> <span class="hljs-string">Front</span> <span class="hljs-string">Row)</span>
            <span class="hljs-attr">accessLevel:</span>
              <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
              <span class="hljs-attr">description:</span> <span class="hljs-string">Access</span> <span class="hljs-string">level</span> <span class="hljs-string">granted</span> <span class="hljs-string">(e.g.,</span> <span class="hljs-string">Basic,</span> <span class="hljs-string">Full)</span>
            <span class="hljs-attr">exclusiveAccess:</span>
              <span class="hljs-attr">type:</span> <span class="hljs-string">boolean</span>
              <span class="hljs-attr">description:</span> <span class="hljs-string">Exclusive</span> <span class="hljs-string">access</span> <span class="hljs-string">granted</span> <span class="hljs-string">to</span> <span class="hljs-string">certain</span> <span class="hljs-string">users</span>

    <span class="hljs-attr">UpdateSessionPreferences:</span>
      <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
      <span class="hljs-attr">properties:</span>
        <span class="hljs-attr">userId:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">integer</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Unique</span> <span class="hljs-string">ID</span> <span class="hljs-string">of</span> <span class="hljs-string">the</span> <span class="hljs-string">user</span>
        <span class="hljs-attr">preferences:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
          <span class="hljs-attr">properties:</span>
            <span class="hljs-attr">seating:</span>
              <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
              <span class="hljs-attr">description:</span> <span class="hljs-string">New</span> <span class="hljs-string">seating</span> <span class="hljs-string">preference</span> <span class="hljs-string">(e.g.,</span> <span class="hljs-string">VIP)</span>
            <span class="hljs-attr">accessLevel:</span>
              <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
              <span class="hljs-attr">description:</span> <span class="hljs-string">Updated</span> <span class="hljs-string">access</span> <span class="hljs-string">level</span> <span class="hljs-string">(e.g.,</span> <span class="hljs-string">Full)</span>
            <span class="hljs-attr">exclusiveAccess:</span>
              <span class="hljs-attr">type:</span> <span class="hljs-string">boolean</span>
              <span class="hljs-attr">description:</span> <span class="hljs-string">Updated</span> <span class="hljs-string">access</span> <span class="hljs-string">preference</span> <span class="hljs-string">(e.g.,</span> <span class="hljs-literal">true</span> <span class="hljs-string">or</span> <span class="hljs-literal">false</span><span class="hljs-string">)</span>
</code></pre>
<p>In this file, you have a single <code>/register-session</code> path with multiple methods. We define just one path, <code>/register-session</code>, and list the <code>GET</code> and <code>PATCH</code> methods under it. This keeps the spec clean, reduces duplication, and shows that these methods relate to the same resource.</p>
<p>Here's a visual representation of this OAS file using the Swagger editor:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730414663261/680dd222-8d36-41be-a73a-10a55771d906.png" alt="Multiple methods in one operation in an OpenAPI spec" width="2940" height="1596" loading="lazy"></p>
<h2 id="heading-api-documentation-tips"><strong>API Documentation Tips</strong></h2>
<p>While working with OpenAPI specifications, there are some useful tricks and tips that can make your OAS file more readable and maintainable.</p>
<h3 id="heading-use-markdown-in-openapi"><strong>Use Markdown in OpenAPI</strong></h3>
<p>OpenAPI specifications allow the use of Markdown in the <code>description</code> field. The version of Markdown used in OAS is called CommonMark, the same version used in GitHub.</p>
<p>Markdown formatting allows you to make text more visually engaging and organized. You can add formatting such as headers, lists, code blocks, bold, italics, and so on, which can make your documentation easier to scan and more accessible for readers.</p>
<p>For example, if you need to emphasize certain parts of an endpoint's purpose or highlight important details, Markdown lets you do it naturally.</p>
<p>You can add Markdown directly into any <code>description</code> field in the OpenAPI file, like this:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">paths:</span>
  <span class="hljs-string">/register-session:</span>
    <span class="hljs-attr">get:</span>
      <span class="hljs-attr">description:</span> <span class="hljs-string">|
        ## Retrieve Session Registration
</span>       <span class="hljs-string">Retrieves</span> <span class="hljs-string">the</span> <span class="hljs-string">registration</span> <span class="hljs-string">details</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">specific</span> <span class="hljs-string">user.</span>  
       <span class="hljs-bullet">-</span> <span class="hljs-string">**Note:**</span> <span class="hljs-string">This</span> <span class="hljs-string">data</span> <span class="hljs-string">includes</span> <span class="hljs-string">seat</span> <span class="hljs-string">assignment</span> <span class="hljs-string">and</span> <span class="hljs-string">access</span> <span class="hljs-string">level.</span>  
       <span class="hljs-bullet">-</span> <span class="hljs-attr">Example of JSON response:</span> <span class="hljs-string">`{"userId":</span> <span class="hljs-number">789</span><span class="hljs-string">,</span> <span class="hljs-attr">"sessionId":</span> <span class="hljs-number">1234</span><span class="hljs-string">,</span> <span class="hljs-attr">"seating":</span> <span class="hljs-string">"General"</span><span class="hljs-string">}`</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'200':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Registration</span> <span class="hljs-string">details</span> <span class="hljs-string">retrieved</span> <span class="hljs-string">successfully</span>
</code></pre>
<p>When deployed to supported documentation platforms like <a target="_blank" href="https://rapidocweb.com/">RapiDoc</a> or <a target="_blank" href="https://readme.com/">ReadMe</a>, this will render beautifully with all your Markdown styling intact.</p>
<p>Here's a deployed version of this example in Readme:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730414669217/100bcde8-a25e-4eab-9102-5113476a334b.png" alt="Markdown in description field on Readme" width="1358" height="906" loading="lazy"></p>
<h3 id="heading-use-the-operationid-field"><strong>Use the</strong> <code>operationId</code> Field</h3>
<p>The <code>operationId</code> field is an optional field in OpenAPI specs that assigns a unique name to each API operation.</p>
<p>It is an identifier that you can use to call specific methods when integrating with SDKs or when linking between parts of your documentation.</p>
<p>By effectively using the <code>operationId</code>, you make it much easier for developers to reference specific actions in the API, which is especially helpful when the API is complex or has multiple endpoints.</p>
<p>Place the <code>operationId</code> inside each HTTP method block to give it a unique identifier. For instance:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">paths:</span>
  <span class="hljs-string">/register-session:</span>
    <span class="hljs-attr">get:</span>
      <span class="hljs-attr">operationId:</span> <span class="hljs-string">getSessionRegistration</span>
      <span class="hljs-attr">description:</span> <span class="hljs-string">Retrieve</span> <span class="hljs-string">a</span> <span class="hljs-string">registration</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">conference</span> <span class="hljs-string">session</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'200':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Successfully</span> <span class="hljs-string">retrieved</span> <span class="hljs-string">the</span> <span class="hljs-string">registration</span>
    <span class="hljs-attr">patch:</span>
      <span class="hljs-attr">operationId:</span> <span class="hljs-string">updateSessionRegistration</span>
      <span class="hljs-attr">description:</span> <span class="hljs-string">Update</span> <span class="hljs-string">a</span> <span class="hljs-string">registration</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">conference</span> <span class="hljs-string">session</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'200':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Registration</span> <span class="hljs-string">updated</span> <span class="hljs-string">successfully</span>
</code></pre>
<p>With the <code>operationId</code>, developers can directly refer to <code>getSessionRegistration</code> or <code>updateSessionRegistration</code> as function calls in code or API clients.</p>
<h3 id="heading-use-ref-for-reusable-components"><strong>Use</strong> <code>$ref</code> for Reusable Components</h3>
<p>The <code>$ref</code> keyword in OpenAPI lets you create and reuse components across your spec file. This technique is especially helpful when you have similar request bodies, responses, or parameters repeated in multiple endpoints.</p>
<p>By defining components in a single place and referencing them as needed, you avoid redundancy, reduce errors, and facilitate updates.</p>
<p>So, instead of updating the same parameter across multiple locations, you update it once in the reusable component, and every endpoint using it gets the update.</p>
<p>To use it, first define the reusable component in the components section of your OpenAPI file, then reference it elsewhere using <code>$ref</code>:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">components:</span>
  <span class="hljs-attr">schemas:</span>
    <span class="hljs-attr">RegistrationDetails:</span>
      <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
      <span class="hljs-attr">properties:</span>
        <span class="hljs-attr">userId:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">integer</span>
        <span class="hljs-attr">sessionId:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">integer</span>
        <span class="hljs-attr">seating:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
<span class="hljs-attr">paths:</span>
  <span class="hljs-string">/register-session:</span>
    <span class="hljs-attr">post:</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">Register</span> <span class="hljs-string">for</span> <span class="hljs-string">a</span> <span class="hljs-string">session</span>
      <span class="hljs-attr">requestBody:</span>
        <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>
        <span class="hljs-attr">content:</span>
          <span class="hljs-attr">application/json:</span>
            <span class="hljs-attr">schema:</span>
              <span class="hljs-string">$ref:</span> <span class="hljs-string">'#/components/schemas/RegistrationDetails'</span>
      <span class="hljs-attr">responses:</span>
        <span class="hljs-attr">'201':</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Successfully</span> <span class="hljs-string">registered</span> <span class="hljs-string">for</span> <span class="hljs-string">the</span> <span class="hljs-string">session</span>
</code></pre>
<p>In this file, <code>RegistrationDetails</code> is defined once and is referenced using the <code>$ref</code> keyword in the <code>/register-session</code> operation.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>In this article, you learned how to resolve some complex use cases you might encounter when documenting your API reference using an OpenAPI specification. We went through what to do when you have a single endpoint with multiple methods or duplicate endpoints.</p>
<p>Creating your API reference without an OpenAPI spec file is a manual approach that can become redundant if you have to replicate it across various platforms. But by relying on the tips from the article, you're sure to create better, more efficient, and more reusable OpenAPI specifications. And these, in turn, will lead to better API documentation.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Documentation Site using React and Docusaraus ]]>
                </title>
                <description>
                    <![CDATA[ Having a properly designed and comprehensive documentation site is important for any project. But creating good documentation can be challenging, and problems like poor user onboarding experience and increased support tickets can become daily hassles... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-documentation-site-using-react-and-docusaraus/</link>
                <guid isPermaLink="false">6706b5ae1446e5644b75b252</guid>
                
                    <category>
                        <![CDATA[ docusaurus ]]>
                    </category>
                
                    <category>
                        <![CDATA[ documentation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Technical writing  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chisom Uma ]]>
                </dc:creator>
                <pubDate>Wed, 09 Oct 2024 16:56:14 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1728407506914/49f6f7cd-af92-405e-ac89-d71bd74e3f18.avif" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Having a properly designed and comprehensive documentation site is important for any project. But creating good documentation can be challenging, and problems like poor user onboarding experience and increased support tickets can become daily hassles for a team.</p>
<p>This is why documentation tools like Docusaurus are great for helping you create visually stunning documentation sites in abo 5 minutes.</p>
<p>In this tutorial, I'll show you how to build a documentation site using React and Docusaurus.</p>
<p>If you are new to Docusaurus, you are probably wondering, “why React?, why not any other framework like Next.js?”, Don’t worry – I’ll also answer this question in this guide.</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-what-is-docusaurus">What is Docusaurus?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-getting-started-and-installation">Getting Started and Installation</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-project-structure">Project structure</a></li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-start-your-docusaurus-website">How to Start Your Docusaurus Website</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-documentation-overview">How to Create Documentation (Overview)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-mdx-and-react-components">MDX and React Components</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-tabs">Tabs</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-alerts-or-admonitions">Alerts or Admonitions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-code-blocks">Code blocks</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-docusaurus-blog">Docusaurus Blog</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-custom-pages">Custom Pages</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-styling-in-docusaurus">Styling in Docusaurus</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-deployment">Deployment</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow along with this guide, you should have:</p>
<ul>
<li><p><strong>Node.js</strong> version 18.0 or above installed</p>
</li>
<li><p>Basic knowledge of React and Markdown</p>
</li>
<li><p>IDE (preferably VSCode)</p>
</li>
</ul>
<h2 id="heading-what-is-docusaurus">What is Docusaurus?</h2>
<p><a target="_blank" href="https://docusaurus.io/">Docusaurus</a> was released by the Meta Open Source team in 2017 to help devs build, deploy, and maintain documentation websites easily and quickly. The project’s other goal was to improve the speed of both developers and end users using the <a target="_blank" href="https://web.dev/articles/apply-instant-loading-with-prpl">PRPL</a> pattern.</p>
<p>Some of its cool features include search and localization, powered by <a target="_blank" href="https://www.algolia.com/">Algolia</a> (search) and <a target="_blank" href="https://crowdin.com/">Crowdin</a> (language support and internationalization).</p>
<p>Now, let’s talk about why we’re using React for this tutorial. Well, Docusaurus is built on top of React, which makes it easy to customize the website. Also, Docusaurus supports Markdown and MDX (which lets you use React JSX in your markdown content).</p>
<p>As a technical writer myself, I love that this tool supports Markdown, which I'm quite familiar with. It allows me to focus on creating helpful documentation without worrying about converting the text to other text formats.</p>
<h2 id="heading-getting-started-and-installation">Getting Started and Installation</h2>
<p>Getting started with Docusaraus is pretty straightforward. The first thing you need to do is head over to your terminal and run the command below:</p>
<pre><code class="lang-plaintext">npx create-docusaurus@latest my-website classic
</code></pre>
<p><strong>Note:</strong> The Docusaurus team recommends the <code>classic</code> template because it is easier to get started with fast. It also contains <code>@docusaurus/preset-classic</code> – which includes standard documentation, a blog, custom pages, and a CSS framework (with dark mode support).</p>
<h3 id="heading-project-structure">Project structure</h3>
<p>After installation, this is what your newly created Docusaurus project structure should look like:</p>
<pre><code class="lang-plaintext">📦my-website
┣ 📂blog
┃ ┣ 📂2021-08-26-welcome
┃ ┃ ┣ 📜docusaurus-plushie-banner.jpeg
┃ ┃ ┗ 📜index.md
┃ ┣ 📜2019-05-28-first-blog-post.md
┃ ┣ 📜2019-05-29-long-blog-post.md
┃ ┣ 📜2021-08-01-mdx-blog-post.mdx
┃ ┣ 📜authors.yml
┃ ┗ 📜tags.yml
┣ 📂docs
┃ ┣ 📂tutorial-basics
┃ ┃ ┣ 📜congratulations.md
┃ ┃ ┣ 📜create-a-blog-post.md
┃ ┃ ┣ 📜create-a-document.md
┃ ┃ ┣ 📜create-a-page.md
┃ ┃ ┣ 📜deploy-your-site.md
┃ ┃ ┣ 📜markdown-features.mdx
┃ ┃ ┗ 📜_category_.json
┃ ┣ 📂tutorial-extras
┃ ┃ ┣ 📂img
┃ ┃ ┃ ┣ 📜docsVersionDropdown.png
┃ ┃ ┃ ┗ 📜localeDropdown.png
┃ ┃ ┣ 📜manage-docs-versions.md
┃ ┃ ┣ 📜translate-your-site.md
┃ ┃ ┗ 📜_category_.json
┃ ┗ 📜intro.md
┣ 📂src
┃ ┣ 📂components
┃ ┃ ┗ 📂HomepageFeatures
┃ ┃ ┃ ┣ 📜index.js
┃ ┃ ┃ ┗ 📜styles.module.css
┃ ┣ 📂css
┃ ┃ ┗ 📜custom.css
┃ ┗ 📂pages
┃ ┃ ┣ 📜index.js
┃ ┃ ┣ 📜index.module.css
┃ ┃ ┗ 📜markdown-page.md
┣ 📂static
┃ ┣ 📂img
┃ ┃ ┣ 📜docusaurus-social-card.jpg
┃ ┃ ┣ 📜docusaurus.png
┃ ┃ ┣ 📜favicon.ico
┃ ┃ ┣ 📜logo.svg
┃ ┃ ┣ 📜undraw_docusaurus_mountain.svg
┃ ┃ ┣ 📜undraw_docusaurus_react.svg
┃ ┃ ┗ 📜undraw_docusaurus_tree.svg
┃ ┗ 📜.nojekyll
┣ 📜.gitignore
┣ 📜babel.config.js
┣ 📜docusaurus.config.js
┣ 📜package.json
┣ 📜README.md
┗ 📜sidebars.js
</code></pre>
<p>Now, let’s explore some of the main directories:</p>
<ul>
<li><p><code>blog/</code>: This is where you store your blog posts</p>
</li>
<li><p><code>docs/</code>: As the name implies, this is where your documentation projects are kept</p>
</li>
<li><p><code>src/</code>: This directory allows you to customize your website further using React code.</p>
</li>
<li><p><code>static</code>: Here, you store static files like images, logos, favicons, and so on.</p>
</li>
</ul>
<p>A very important file is the <code>docusaurus.config.js</code> file, which acts as the main configuration file for your website.</p>
<h2 id="heading-how-to-start-your-docusaurus-website">How to Start Your Docusaurus Website</h2>
<p>To run your website locally, first open a new terminal on your IDE and run the following command below to start the development server:</p>
<pre><code class="lang-plaintext">cd my-website

npm i

npx docusaurus start
</code></pre>
<p>After running the above command, the browser compiles the React and Markdown files and starts a local development server at <a target="_blank" href="http://localhost:3000/">http://localhost:3000/</a>. Docusaurus enables hot reload, so you can see changes made to your React, Markdown, and MDX files automatically – without reloading your entire app.</p>
<p>Here is what the site looks like on your browser:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728389930307/b0bd7810-dea2-458b-85a1-e10b9a7b3028.png" alt="Bootstrapped Docusaurus site" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>In the image above, you are first welcomed to an intuitive and easily navigable website. At the top left corner of the website, you will see the “<strong>Tutorial</strong>” and “<strong>Blog</strong>” sections.</p>
<ul>
<li><p><strong>Tutorial:</strong> This is where users can see the live version of your documentation.</p>
</li>
<li><p><strong>Blog:</strong> This is where users can see the live version of your blog.</p>
</li>
</ul>
<p>The link to the Docusaurus Open Source GitHub repo and the icon for toggling your website between light and dark modes are at the top-right corner of the site.</p>
<p>Alternatively, you can use <a target="_blank" href="https://docusaurus.io/docs/playground">docusaurus.new</a> to test Docusaurus quickly in a playground without having to go through the installation process. Here, you have an option to choose between <a target="_blank" href="https://codesandbox.io/">CodeSandbox</a> and <a target="_blank" href="https://stackblitz.com/">StackBlitz</a>.</p>
<h2 id="heading-how-to-create-documentation-overview">How to Create Documentation (Overview)</h2>
<p>Let’s take a closer look at our <code>docs</code> folder. If we head back to our local site and click on “<strong>Tutorial</strong>,” we will see some pre-built doc pages, as shown below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728390116953/ec281a01-5b0c-413a-83b9-36d0352f3e03.png" alt="Documentation overview on the site" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>These documentation pages are reflected in the <code>docs</code> folder located in your IDE. When we open the <code>category.json</code> page, we can adjust the name or position of each page. This means you don’t have to name the folders the same as the page name, since the name of the file will be the URL of the page.</p>
<p>To create our new documentation, let’s use the following steps:</p>
<ol>
<li><p>Delete all the files in the docs folder. Your browser and terminal will typically display an error after this.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728390294195/5a59bdc4-7a79-4b17-9e85-630867c6c3ec.png" alt="Error from deleted files" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p> This is because we need at least one page in the docs folder.</p>
</li>
<li><p>Create a new file inside the docs folder, which can be named anything you prefer, but in our case, I named it <a target="_blank" href="http://single-page.md">single-page.md</a>. You should see this change immediately reflected when you go to your local website. This is what you will see in the documentation pages section:</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728390512797/90b86f29-8b03-414b-acff-18842cd4c462.png" alt="Single page " class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
</li>
</ol>
<p>Inside this newly created file, you can create your documentation seamlessly. The image above shows the little Markdown content I created saying “Single Page” in an H1 and “This is a single page” in plain text.</p>
<p>Let’s say you want to create a more organized content structure, but you don’t know how to get started. Here are a few simple steps on how to do that:</p>
<ol>
<li><p>Create a new folder inside the <code>docs</code> folder, named “Getting Started”</p>
</li>
<li><p>Create new Markdown files inside the “Getting started” folder, and name them whatever you like. For the sake of this tutorial, let’s name it <a target="_blank" href="http://API-docs.md"><code>API-docs.md</code></a> and <a target="_blank" href="http://Session-replay.md"><code>Session-replay.md</code></a>.</p>
</li>
<li><p>Write your documentation in Markdown</p>
</li>
</ol>
<p>This is how the file structure should look like on your IDE:</p>
<pre><code class="lang-plaintext">📦docs
┣ 📂Getting started
┃ ┣ 📜Open-replay.md
┃ ┗ 📜Session-replay.md
┗ 📜single-page.md
</code></pre>
<p>Here is a simple GIF to demonstrate how this works on the local documentation website.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728390784981/9eed8cbf-c6dc-4508-9d75-401d87673cf7.gif" alt="GIF showing local documentation site" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Now, let’s try to create a separate page in the <code>doc</code> folder. To do this, let’s create a  <code>category.json</code> page in the <code>Getting started</code> folder. Inside the  <code>category.json</code>  file, we will include the following JSON text:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"label"</span>: <span class="hljs-string">"Custom Title"</span>,
  <span class="hljs-attr">"position"</span>: <span class="hljs-number">2</span>,
  <span class="hljs-attr">"link"</span>: {
    <span class="hljs-attr">"type"</span>: <span class="hljs-string">"generated-index"</span>,
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"This is a custom description"</span>
  }
}
</code></pre>
<ul>
<li><p>The <code>link</code> object creates a separate page for the folder.</p>
</li>
<li><p>The <code>type</code> property is set to generated-index, which means it will generate the pages with all the sub-pages.</p>
</li>
<li><p>The <code>description</code> property is a description of the page that will show up beneath the title.</p>
</li>
</ul>
<p>When you check out your local hosted documentation site, you will see that the label has changed, and a separate page has been created for the folder.</p>
<p>In this section, I will show you an example of how headings and tables of content work in Docusaurus.</p>
<p>The first thing I did was create a <a target="_blank" href="http://markdown.md"><code>markdown.md</code></a> file. Then I pasted a couple of headings in Markdown text format right inside the file, like this:</p>
<pre><code class="lang-markdown">---
title: Basic Markdown
<span class="hljs-section">sidebar<span class="hljs-emphasis">_position: 1
---

# Heading 1

## Heading 2

### Heading 3

#### Heading 4

##### Heading 5

###### Heading 6</span></span>
</code></pre>
<p>When we head back to our website, we can see that only headings level 2 and 3 are automatically added, just as shown below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728391234366/1ba2a824-3d31-4fd2-bd3e-8fcb9547e073.png" alt="Image showing headers" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>We can edit to ensure that all the headings show up. To do this, first create a <a target="_blank" href="http://table-of-contents.md"><code>table-of-contents.md</code></a>, then paste in the following Markdown:</p>
<pre><code class="lang-markdown">---
title: Table of Contents
sidebar<span class="hljs-emphasis">_position: 2
toc_</span>min<span class="hljs-emphasis">_heading_</span>level: 2
<span class="hljs-section">toc<span class="hljs-emphasis">_max_</span>heading<span class="hljs-emphasis">_level: 6
---

import TOCInline from '@theme/TOCInline';

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">TOCInline</span> <span class="hljs-attr">toc</span>=<span class="hljs-string">{toc}</span> <span class="hljs-attr">minHeadingLevel</span>=<span class="hljs-string">{2}</span> <span class="hljs-attr">maxHeadingLevel</span>=<span class="hljs-string">{6}</span> /&gt;</span></span>

## Heading 2

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor.

### Heading 3

Some content here.

#### Heading 4

Some content here.

##### Heading 5

Some content here.

###### Heading 6

Some content here.

## Heading 2

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor.

### Heading 3

Some content here.

#### Heading 4

Some content here.

##### Heading 5

Some content here.

###### Heading 6

Some content here.

## Heading 2

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor.

### Heading 3

Some content here.

#### Heading 4

Some content here.

##### Heading 5

Some content here.

###### Heading 6

Some content here.</span></span>
</code></pre>
<p>I added a TOC property and set the minimum and maximum heading levels with <code>toc_min_heading_level: 2</code> and <code>toc_max_heading_level: 6</code>. We also added an inline table of contents by first importing <code>TOCInline</code> from <code>@theme/TOCInline</code>. Then, we created a TOCInline component (which can be put anywhere you want your TOC to show up).</p>
<p>Now, all the headings show up in the table of contents part of the website:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728398728652/37595594-3dbc-42bc-853c-f5b5ba9714c4.png" alt="Image showing table of content and headers" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Beautiful right?</p>
<h2 id="heading-mdx-and-react-components">MDX and React Components</h2>
<p>Now, let’s talk about one of the most exciting features of Docusaurus: MDX and React components.</p>
<p>You might ask – how can Docusaurus use <code>TOC</code> or <code>import</code> in the Markdown file? Well, that’s because Docusaurus uses MDX, which is basically Markdown with JSX.</p>
<p>To demonstrate this, let’s create a new Markdown file inside our <code>Getting started</code> folder titled  <a target="_blank" href="http://MDX.md"><code>MDX.md</code></a> , then we create a separate file inside the <code>src</code> &gt; <code>components</code> folder and name the file <code>Tag.js</code> . Then we paste in the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Tag</span>(<span class="hljs-params">{ children, color }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span>
      <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span>
        <span class="hljs-attr">backgroundColor:</span> <span class="hljs-attr">color</span>,
        <span class="hljs-attr">borderRadius:</span> '<span class="hljs-attr">4px</span>',
        <span class="hljs-attr">color:</span> '#<span class="hljs-attr">fff</span>',
        <span class="hljs-attr">padding:</span> '<span class="hljs-attr">0.2rem</span> <span class="hljs-attr">0.5rem</span>',
        <span class="hljs-attr">fontWeight:</span> '<span class="hljs-attr">bold</span>',
      }}
    &gt;</span>
      {children}
    <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>
  );
}
</code></pre>
<p>Here, we first imported the core React library, and then we defined a functional component called Tag, which takes in two props as input: <code>children</code> and <code>color</code>. In our return statement, we included our CSS styles for the <code>span</code> element.</p>
<p>Inside the <a target="_blank" href="http://MDX.md">MDX.md</a> file, paste in the below code:</p>
<pre><code class="lang-markdown">---
title: MDX
<span class="hljs-section">sidebar<span class="hljs-emphasis">_position: 3
---

import Tag from '@site/src/components/Tag';

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Tag</span> <span class="hljs-attr">color</span>=<span class="hljs-string">"#FF5733"</span>&gt;</span></span>Important<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">Tag</span>&gt;</span></span> information: This is an <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Tag</span> <span class="hljs-attr">color</span>=<span class="hljs-string">"#3399FF"</span>&gt;</span></span>Exciting<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">Tag</span>&gt;</span></span> example of custom components!

I can write <span class="hljs-strong">**Markdown**</span> alongside my _</span>JSX<span class="hljs-emphasis">_!</span></span>
</code></pre>
<p>Here, we import <code>Tag</code> from our components folder. This is what it looks like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728398996580/a826b80c-1862-46f4-b111-dc6366dd13db.png" alt="Image showing how MDX works" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p><strong>Note:</strong> Because we use MDX, Docusaurus comes with pre-built components like Tabs, alerts, code blocks, and more. Let’s check them out in the following subsections.</p>
<h3 id="heading-tabs">Tabs</h3>
<p>In this subsection, we’ll talk about tabs as a pre-built component in Docusaurus. Let’s dive right in!</p>
<p>For a start, let’s create a new Markdown file called <a target="_blank" href="http://tabs.md"><code>tabs.md</code></a> and paste in the following code:</p>
<pre><code class="lang-markdown">---
title: Tabs in Markdown
<span class="hljs-section">sidebar<span class="hljs-emphasis">_position: 4
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Tabs</span>&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">TabItem</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"book"</span> <span class="hljs-attr">label</span>=<span class="hljs-string">"Book"</span> <span class="hljs-attr">default</span>&gt;</span></span>
    Dive into the world of knowledge with a captivating book 📚
  <span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">TabItem</span>&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">TabItem</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"painting"</span> <span class="hljs-attr">label</span>=<span class="hljs-string">"Painting"</span>&gt;</span></span>
    Admire the strokes of artistry on a beautiful painting 🖼️
  <span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">TabItem</span>&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">TabItem</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"music"</span> <span class="hljs-attr">label</span>=<span class="hljs-string">"Music"</span>&gt;</span></span>
    Let the soothing melodies of music transport you 🎶
  <span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">TabItem</span>&gt;</span></span>
<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">Tabs</span>&gt;</span></span>

I'm a text that doesn't belong to any tab. So I'm always visible.</span></span>
</code></pre>
<p>We imported <code>Tabs</code> from <code>@theme/Tabs</code> and <code>TabItem</code> from <code>@theme/TabItem</code>. Then, we created a Tabs component, which is the container, and the <code>TabItem</code> component is the tab itself. The <code>value</code> property is the value of the tab, while the <code>label</code> property is the label of the tab. The default property defines which tab is open by default – in this case, the “Book” tab.</p>
<p>This is how it looks:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728399214390/edece46f-3357-4b96-8672-a462a8a8916b.png" alt="Image showing how tabs work" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Each tab is clickable and transitions smoothly.</p>
<h3 id="heading-alerts-or-admonitions">Alerts or Admonitions</h3>
<p>Docusaurus comes with pre-built alerts or admonitions. To create an alert, you simply wrap the text with three colons and follow it with the type of alert you want the reader to have in mind.</p>
<p>Let’s create a new Markdown file called <a target="_blank" href="http://alerts.md">alerts.md</a> and paste in the following Markdown:</p>
<pre><code class="lang-markdown">---
title: Alerts or Admonitions
<span class="hljs-section">sidebar<span class="hljs-emphasis">_position: 5
---

:::note

Here's some <span class="hljs-strong">**information**</span> with _</span>Markdown<span class="hljs-emphasis">_ styling.

:::

:::tip

Here's a <span class="hljs-strong">**helpful tip**</span> with _</span>formatted text<span class="hljs-emphasis">_.

:::

:::info

Here's some <span class="hljs-strong">**useful info**</span> presented in a clear way.

:::

:::caution

Please take <span class="hljs-strong">**extra caution**</span> with this important note.

:::

:::danger

This is a <span class="hljs-strong">**dangerous situation**</span> you need to be aware of.

:::

:::note This is a _</span>custom title<span class="hljs-emphasis">_

And you can add images as well.

![<span class="hljs-string">alt text</span>](<span class="hljs-link">https://picsum.photos/600/400</span>)

:::</span></span>
</code></pre>
<p>The various types of alerts, as shown in the Markdown above, are:</p>
<ul>
<li><p><code>note</code></p>
</li>
<li><p><code>tip</code></p>
</li>
<li><p><code>info</code></p>
</li>
<li><p><code>caution</code></p>
</li>
<li><p><code>danger</code></p>
</li>
</ul>
<p>Here’s what it looks like on the website:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728402667575/cc10af2d-e417-4426-985b-4aad81a082db.png" alt="Image showing how alerts and admonitions work" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Alerts and admonitions are pretty common in documentation sites. So, if you have ever wondered how it’s been done, well, this is it! It’s quite straightforward.</p>
<h3 id="heading-code-blocks">Code blocks</h3>
<p>As we already know by now, Docusaurus supports MDX, which allows us to include code blocks in our files. Code blocks are text blocks wrapped around by strings of three backticks. You can add the name of the language after the last of the three backticks.</p>
<p>Let’s create a <a target="_blank" href="http://codeblocks.md"><code>codeblocks.md</code></a> file and paste the following JSX code:</p>
<pre><code class="lang-javascript">---
title: Codeblocks
<span class="hljs-attr">sidebar_position</span>: <span class="hljs-number">6</span>
---

<span class="hljs-string">``</span><span class="hljs-string">`jsx title="Codeblock"
function Greeting(props) {
  return &lt;p&gt;Welcome, {props.userName}!&lt;/p&gt;;
}

export default Greeting;
`</span><span class="hljs-string">``</span>

<span class="hljs-string">``</span><span class="hljs-string">`jsx title="Highlight Lines"
function HighlightText(highlight) {
  if (highlight) {
    // highlight-next-line
    return 'This text is highlighted!';
  }
  return 'Nothing highlighted';
}

function HighlightMoreText(highlight) {
  // highlight-start
  if (highlight) {
    return 'This range is highlighted!';
  }
  // highlight-end
  return 'Nothing highlighted';
}
`</span><span class="hljs-string">``</span>

<span class="hljs-string">``</span><span class="hljs-string">`jsx title="Line Numbers" showLineNumbers
import React from 'react';

function UserProfile(props) {
  const { username, email, isAdmin } = props;

  return (
    &lt;div&gt;
      &lt;h1&gt;User Profile&lt;/h1&gt;
      &lt;p&gt;Username: {username}&lt;/p&gt;
      &lt;p&gt;Email: {email}&lt;/p&gt;
      {isAdmin &amp;&amp; &lt;p&gt;Admin User&lt;/p&gt;}
    &lt;/div&gt;
  );
}

export default UserProfile;
`</span><span class="hljs-string">``</span>

<span class="hljs-string">``</span><span class="hljs-string">`jsx title="Line Numbers with Highlight" {4,9-11} showLineNumbers
import React from 'react';

function UserProfile(props) {
  const { username, email, isAdmin } = props;

  return (
    &lt;div&gt;
      &lt;h1&gt;User Profile&lt;/h1&gt;
      &lt;p&gt;Username: {username}&lt;/p&gt;
      &lt;p&gt;Email: {email}&lt;/p&gt;
      {isAdmin &amp;&amp; &lt;p&gt;Admin User&lt;/p&gt;}
    &lt;/div&gt;
  );
}

export default UserProfile;</span>
</code></pre>
<p>This is what the code blocks look like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728402852316/4873fccd-d4b7-45d7-8d4f-49fea5a3da49.png" alt="Image showing how codeblocks work" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>You can easily copy the code by hovering your cursor over the code blocks and clicking on the copy icon on the top-right side of the code block.</p>
<p><strong>Note:</strong> By default, Docusaurus uses <code>Prism</code> for syntax highlighting.</p>
<p>If you also want to highlight some lines of code, you can do that by adding a comment like this:</p>
<pre><code class="lang-javascript">    <span class="hljs-comment">// highlight-next-line</span>
    <span class="hljs-keyword">return</span> <span class="hljs-string">'This text is highlighted!'</span>;
  }
  <span class="hljs-keyword">return</span> <span class="hljs-string">'Nothing highlighted'</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">HighlightMoreText</span>(<span class="hljs-params">highlight</span>) </span>{
  <span class="hljs-comment">// highlight-start</span>
  <span class="hljs-keyword">if</span> (highlight) {
    <span class="hljs-keyword">return</span> <span class="hljs-string">'This range is highlighted!'</span>;
  }
  <span class="hljs-comment">// highlight-end</span>
</code></pre>
<ul>
<li><p><code>highlight-next-line</code>: allows you to highlight a single line of code</p>
</li>
<li><p><code>highlight-start and highlight-end</code>: allows you to highlight a range of lines</p>
</li>
</ul>
<h2 id="heading-docusaurus-blog">Docusaurus Blog</h2>
<p>The Docusaurus blog also comes by default with the <code>classic</code> template. If you don’t have a blog, you can add the following lines to your <code>docusaurus.config.js</code> file:</p>
<pre><code class="lang-javascript">{
  <span class="hljs-attr">label</span>: <span class="hljs-string">'Blog'</span>,
  <span class="hljs-attr">to</span>: <span class="hljs-string">'/blog'</span>,
}
</code></pre>
<p>Usually, this line should already be in your config file after installing Docusaurus for the first time.</p>
<p>The Docusaurus blog is very simple to understand. Navigate to the blog folder in the project explorer, and you’ll see all the blog posts, which are MDX files. The blog post date should be included on the file name as shown below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728403567052/c60d665d-f29b-433e-bd10-b86542d0063e.png" alt="Image showing blog folder" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>When you open one of the blog posts, you see something like this:</p>
<pre><code class="lang-markdown">---
slug: long-blog-post
title: Long Blog Post
authors: yangshun
<span class="hljs-section">tags: [hello, docusaurus]
---</span>

This is the summary of a very long blog post,

Use a <span class="hljs-code">`&lt;!--`</span> <span class="hljs-code">`truncate`</span> <span class="hljs-code">`--&gt;`</span> comment to limit blog post size in the list view.

<span class="xml"><span class="hljs-comment">&lt;!-- truncate --&gt;</span></span>

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
</code></pre>
<ul>
<li><p><code>slug</code>: You can add a slug to the URL of the blog post</p>
</li>
<li><p><code>title</code>: Title of the blog post</p>
</li>
<li><p><code>authors</code>: The authors of the blog post</p>
</li>
<li><p><code>tags</code>: Tags related to the blog post</p>
</li>
</ul>
<p>In the blog post, we can also use all the Markdown features plus JSX as we have seen before.</p>
<h2 id="heading-custom-pages">Custom Pages</h2>
<p>Technically, Docusaurus isn’t just a fancy documentation site generator with a blog. It’s a standard static site generator – which means you can create any page you want.</p>
<p>To see how creating a custom page in Docusaurus works, let’s create an about.js file in the <code>src</code> <strong>&gt;</strong> <code>pages</code> folder and include the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> Layout <span class="hljs-keyword">from</span> <span class="hljs-string">'@theme/Layout'</span>;
<span class="hljs-keyword">import</span> Head <span class="hljs-keyword">from</span> <span class="hljs-string">'@docusaurus/Head'</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">About</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Layout</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>About<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">name</span>=<span class="hljs-string">"description"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"This is the about page"</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">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"red-text"</span>&gt;</span>About<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>This is the about page.<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 class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span></span>
  );
}
</code></pre>
<p>When you go to <a target="_blank" href="http://localhost:3000/about">http://localhost:3000/about</a>, you should see something like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728404291897/394ee43b-2b30-43f8-a8cf-ff260d51e82a.png" alt="Image showing how custom pages work " class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>We can also add the page to the navbar by going to the docusaurus.config.js file and adding a new item to the navbar array. The item looks like this:</p>
<pre><code class="lang-json">{to: 'about', label: 'About', position: 'left'},
</code></pre>
<p>It then appears on the homepage nav menu like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728404457186/25545cf0-a5bf-4561-8dc8-49045c3cfc9d.png" alt="Image showing how custom pages work " class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>You can now style and customize the <code>about.js</code> file any way you’d prefer using React.</p>
<h2 id="heading-styling-in-docusaurus">Styling in Docusaurus</h2>
<p>Let’s look at how you can style your site in Docusaurus. The easiest way is to customize the <code>custom.css</code> file inside the <code>css</code> <strong>&gt;</strong> <code>custom.css</code> file. This is what the file looks like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728404914713/bd2c8b65-52f9-43d4-b0c8-d09ec9562865.png" alt="Image showing how to perform styling" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Here, you can change the whole color schema of the site and different styling to this file.</p>
<p>You can <strong>read more</strong> about this in the <a target="_blank" href="https://docusaurus.io/docs/styling-layout">Docusaurus styling and layout</a> docs.</p>
<h2 id="heading-seo-in-docusaurus">SEO in Docusaurus</h2>
<p>Docusaurus takes SEO very seriously. By default, Docusaurus automatically adds a description title, canonical URL links, and other useful metadata to each page. This can be configured as shown below:</p>
<pre><code class="lang-markdown">---
title: Our First Page
sidebar<span class="hljs-emphasis">_position: 1

description: A short description of this page
image: ../static/img/docusaurus-social-card.jpg
keywords: [keywords, describing, the main topics]
---

# Single Page

This is a single page.</span>
</code></pre>
<p>You can <strong>read more</strong> about this in the <a target="_blank" href="https://docusaurus.io/docs/seo">Docusaurus SEO</a> docs.</p>
<h2 id="heading-deployment">Deployment</h2>
<p>Deployment is pretty easy with Docusaurus. Since it’s a static site, you can deploy it to any static site hosting service. To do this, run the <code>npm run build</code> command on your CLI. This creates a build folder like the one below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728405905725/a7633e46-cb10-4220-bce8-7b12545a124f.png" alt="Image showing build folder for deployment" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Then, you can upload the contents of the build folder to your hosting service.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article, we covered how to build documentation from scratch, how to create, customize, and style pages, and the awesome SEO power of Docusaurus.</p>
<p>Docusaurus is friendly to both developers and technical writers. As a developer, you can focus on customizing the site, while as a technical writer, you can focus on writing the documentation.</p>
<p>I will highly recommend this tool for both startups and established enterprises looking to build stunning documentation sites.</p>
<p>This guide is not exhaustive, but covers everything you need to know about the basics of building a documentation site with React and Docusaurus.</p>
<p>I hope you found it helpful :)</p>
<p>Here’s the link to my <a target="_blank" href="https://github.com/ChisomUma/docusaurus-project">GitHub code</a> for follow-up purposes.</p>
<p>And here’s the main Docusaurus <a target="_blank" href="https://docusaurus.io/docs/docs-introduction">documentation</a> if you’d like to dive in deeper.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
