<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged express</title>
<link>https://coderlegion.com/tag/express</link>
<description>Powered by Question2Answer</description>
<item>
<title>Stop Inventing Custom API Errors: A Guide to RFC 7807 Problem Details</title>
<link>https://coderlegion.com/32579/stop-inventing-custom-api-errors-a-guide-to-rfc-7807-problem-details</link>
<description>&lt;p&gt;RFC 7807 Problem Details is an official technical specification that defines a standard JSON format for describing errors returned by HTTP web APIs. Instead of having every software engineer create their own custom fields like &lt;code&gt;message&lt;/code&gt;, &lt;code&gt;err_code&lt;/code&gt;, or &lt;code&gt;reason&lt;/code&gt;, this standard establishes predictable key names such as &lt;code&gt;type&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;status&lt;/code&gt;, &lt;code&gt;detail&lt;/code&gt;, and &lt;code&gt;instance&lt;/code&gt;. This gives both computer programs and human developers a uniform way to understand exactly what went wrong during an API request.&lt;/p&gt;
&lt;h3 id=&quot;the-everyday-analogy-car-diagnostic-reports&quot;&gt;The Everyday Analogy: Car Diagnostic Reports&lt;/h3&gt;
&lt;p&gt;Imagine taking your car to a mechanic when the check engine light comes on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Option A:&lt;/strong&gt; The mechanic hands you a piece of paper that simply says: &quot;Car is broken. Cost: $300.&quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Option B:&lt;/strong&gt; The mechanic hands you a standardized vehicle diagnostic report that lists the exact sensor code (e.g., P0300), a short title (&quot;Random Cylinder Misfire&quot;), the severity level, specific details (&quot;Cylinder 3 failed 14 times in the past 10 miles&quot;), and a reference link to the manufacturer's repair guide.&lt;/p&gt;
&lt;p&gt;Option A leaves you guessing and forces you to ask ten follow-up questions. Option B gives you instant, structured clarity. RFC 7807 acts as that standardized diagnostic report for your web applications.&lt;/p&gt;
&lt;h3 id=&quot;why-rfc-7807-matters-for-engineers&quot;&gt;Why RFC 7807 Matters for Engineers&lt;/h3&gt;
&lt;p&gt;In modern software engineering, web applications are built by connecting multiple services together. Front-end user interfaces (like React apps) communicate with back-end servers running Express.js, which in turn communicate with databases or external payment systems.&lt;/p&gt;
&lt;p&gt;When something fails—such as an invalid credit card or an out-of-stock product—the back-end server must notify the front-end application. Without a standard like RFC 7807, team A might structure errors as &lt;code&gt;{ &quot;error&quot;: &quot;Invalid payload&quot; }&lt;/code&gt;, while team B uses &lt;code&gt;{ &quot;statusCode&quot;: 400, &quot;description&quot;: &quot;Bad input&quot; }&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This inconsistency causes real operational pain:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Fragile UI Code&lt;/strong&gt;: Front-end developers must write endless conditional &lt;code&gt;if/else&lt;/code&gt; checks to parse different error formats from different endpoints.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wasted Debugging Hours&lt;/strong&gt;: Software engineers spend extra time reading through server logs trying to figure out what a vague error message actually means.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Poor User Experience&lt;/strong&gt;: Applications fail silently or show generic &quot;An unknown error occurred&quot; popups because the front-end couldn't parse the unexpected error structure.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By enforcing RFC 7807, teams agree on a single contract. Front-end apps can use one central error-handling mechanism to extract the exact field that failed and display a meaningful message to the user.&lt;/p&gt;
&lt;h3 id=&quot;code-example-express-js-error-handler&quot;&gt;Code Example: Express.js Error Handler&lt;/h3&gt;
&lt;p&gt;Here is how you can implement an RFC 7807 compliant error handler in a Node.js and Express server. The custom HTTP content type for this standard is &lt;code&gt;application/problem+json&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const express = require('express');
const app = express();

app.use(express.json());

// Route that triggers a validation error
app.post('/api/users', (req, res, next) =&amp;gt; {
  const { email } = req.body;
  if (!email) {
    const error = new Error('Email field is required.');
    error.status = 400;
    error.type = 'https://api.example.com/errors/missing-field';
    error.title = 'Invalid Request Payload';
    error.instance = '/api/users';
    return next(error);
  }
  res.status(201).json({ message: 'User created' });
});

// Central RFC 7807 Error Handling Middleware
app.use((err, req, res, next) =&amp;gt; {
  const status = err.status || 500;
  
  const problemDetails = {
    type: err.type || 'https://api.example.com/errors/internal-error',
    title: err.title || 'Internal Server Error',
    status: status,
    detail: err.message || 'An unexpected error occurred on the server.',
    instance: err.instance || req.originalUrl
  };

  // Set the standard RFC 7807 header
  res.setHeader('Content-Type', 'application/problem+json');
  res.status(status).json(problemDetails);
});

app.listen(3000, () =&amp;gt; console.log('Server running on port 3000'));
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;key-takeaway&quot;&gt;Key Takeaway&lt;/h3&gt;
&lt;p&gt;RFC 7807 transforms unpredictable API errors into predictable, structured data that both machines and humans can process seamlessly. Adopting this standard eliminates entire classes of error-handling bugs, simplifies frontend integration, and establishes clear debugging contracts across your entire software ecosystem.&lt;/p&gt;
&lt;hr&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;a href=&quot;https://www.buymeacoffee.com/saurav_tb_pandey&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer nofollow&quot;&gt;
    &lt;img src=&quot;https://img.shields.io/badge/Buy_Me_A_Coffee-FFDD00?style=for-the-badge&amp;amp;logo=buy-me-a-coffee&amp;amp;logoColor=black&quot; alt=&quot;Buy Me A Coffee&quot;&gt;
&lt;p&gt;  &lt;/p&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Enjoyed this breakdown? Support my open-source work on &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.buymeacoffee.com/saurav_tb_pandey&quot;&gt;Buy Me a Coffee&lt;/a&gt; or explore &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/react-hook-lab&quot;&gt;react-hook-lab&lt;/a&gt;!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Originally published on my blog. You can &lt;a rel=&quot;nofollow&quot; href=&quot;https://sauravtbpandey.blogspot.com/2026/10/how-rfc-7807-problem-details-transforms.html&quot;&gt;read the alternative breakdown here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32579/stop-inventing-custom-api-errors-a-guide-to-rfc-7807-problem-details</guid>
<pubDate>Mon, 26 Oct 2026 17:41:23 +0000</pubDate>
</item>
<item>
<title>Stop Hardcoding Secrets: Master Environment Variables and Dotenv in Node.js</title>
<link>https://coderlegion.com/31504/stop-hardcoding-secrets-master-environment-variables-and-dotenv-in-node-js</link>
<description>&lt;h2 id=&quot;stop-hardcoding-secrets-master-environment-variables-and-dotenv-in-node-js&quot;&gt;Stop Hardcoding Secrets: Master Environment Variables and Dotenv in Node.js&lt;/h2&gt;
&lt;p&gt;When building modern web applications, keeping sensitive information secure is just as critical as writing functional code. One of the most fundamental practices every developer must learn is using environment variables and dotenv configuration.&lt;/p&gt;
&lt;h3 id=&quot;what-are-environment-variables-and-dotenv-configuration-&quot;&gt;What Are Environment Variables and Dotenv Configuration?&lt;/h3&gt;
&lt;p&gt;Environment variables are dynamic key-value pairs stored at the operating system or runtime level outside of an application's codebase. In Node.js development, dotenv configuration refers to utilizing a lightweight library called &lt;code&gt;dotenv&lt;/code&gt; that automatically loads settings from a plain &lt;code&gt;.env&lt;/code&gt; file into Node's runtime environment (&lt;code&gt;process.env&lt;/code&gt;). This mechanism allows developers to customize application behavior and supply secret credentials without modifying source code files.&lt;/p&gt;
&lt;h3 id=&quot;a-real-world-analogy-the-wallet-index-card&quot;&gt;A Real-World Analogy: The Wallet Index Card&lt;/h3&gt;
&lt;p&gt;Imagine you own a custom leather wallet. Instead of permanently embroidering your house address, garage passcode, and emergency contact numbers onto the outer leather where every passerby can read them, you write those sensitive details on a small index card and slide it into an interior pocket.&lt;/p&gt;
&lt;p&gt;When you move to a new apartment or change your passcode, you do not buy a brand-new wallet or destroy the leather stitching. You simply pull out the old index card and slide in an updated one. In this analogy, your wallet is your core application codebase, while the index card inside the pocket acts as your local &lt;code&gt;.env&lt;/code&gt; file holding temporary, site-specific configuration details.&lt;/p&gt;
&lt;h3 id=&quot;why-environment-variables-matter-every-single-day&quot;&gt;Why Environment Variables Matter Every Single Day&lt;/h3&gt;
&lt;p&gt;In professional software engineering, committing raw passwords, secret encryption keys, or API tokens directly into a Git repository is a dangerous vulnerability. Automated web scraping bots constantly crawl public code repositories like GitHub, scanning for exposed database passwords or cloud provider credentials within seconds of a commit. Exposing these keys can lead to compromised database instances, stolen customer data, or massive financial liability from unauthorized cloud usage.&lt;/p&gt;
&lt;p&gt;Furthermore, modern applications run across multiple environments throughout their lifecycle, including local development machines, staging servers for testing, and live production servers handling real user traffic. Environment variables allow engineers to deploy the exact same application code across all these distinct targets. The application simply reads its configuration from whatever local environment variables exist on that specific host server.&lt;/p&gt;
&lt;h3 id=&quot;code-example-configuring-express-with-dotenv&quot;&gt;Code Example: Configuring Express with Dotenv&lt;/h3&gt;
&lt;p&gt;Below is a standard Node.js and Express server setup using the &lt;code&gt;dotenv&lt;/code&gt; package to securely access server configuration values and third-party API credentials.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// Step 1: Import dependencies
// Ensure you have installed express and dotenv via npm: npm install express dotenv
require('dotenv').config();
const express = require('express');

const app = express();

// Step 2: Read configuration values from process.env
const PORT = process.env.PORT || 3000;
const API_KEY = process.env.PAYMENT_GATEWAY_KEY;
const DATABASE_URL = process.env.DATABASE_URL;

// Step 3: Define an Express route utilizing the loaded variables
app.get('/api/status', (req, res) =&amp;gt; {
  if (!API_KEY) {
    return res.status(500).json({ error: 'Missing critical API configuration.' });
  }
  
  res.json({
    status: 'Server is running securely',
    connectedDatabase: DATABASE_URL ? 'Connected' : 'Not Configured',
    environment: process.env.NODE_ENV || 'development'
  });
});

// Step 4: Start the server using the dynamic PORT
app.listen(PORT, () =&amp;gt; {
  console.log(`Server listening on port ${PORT}`);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Along with this JavaScript file, you create a file named &lt;code&gt;.env&lt;/code&gt; in the root folder of your project:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;PORT=5000
NODE_ENV=development
DATABASE_URL=mysql://user:password@localhost:3306/dev_db
PAYMENT_GATEWAY_KEY=secret_live_key_987654321
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Crucially, you must add &lt;code&gt;.env&lt;/code&gt; to your project's &lt;code&gt;.gitignore&lt;/code&gt; file so it is never tracked by version control or pushed to remote repositories.&lt;/p&gt;
&lt;h3 id=&quot;key-takeaway-1&quot;&gt;Key Takeaway&lt;/h3&gt;
&lt;p&gt;Decoupling application code from environment configuration is a foundational pillar of modern, secure software architecture. By adopting environment variables and dotenv configuration early in your development pipeline, you shield your systems from catastrophic credential leaks and build resilient applications ready to run anywhere without friction.&lt;/p&gt;
&lt;hr&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;a href=&quot;https://www.buymeacoffee.com/saurav_tb_pandey&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer nofollow&quot;&gt;
    &lt;img src=&quot;https://img.shields.io/badge/Buy_Me_A_Coffee-FFDD00?style=for-the-badge&amp;amp;logo=buy-me-a-coffee&amp;amp;logoColor=black&quot; alt=&quot;Buy Me A Coffee&quot;&gt;
&lt;p&gt;  &lt;/p&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Enjoyed this breakdown? Support my open-source work on &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.buymeacoffee.com/saurav_tb_pandey&quot;&gt;Buy Me a Coffee&lt;/a&gt; or explore &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/react-hook-lab&quot;&gt;react-hook-lab&lt;/a&gt;!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Originally published on my blog. You can &lt;a rel=&quot;nofollow&quot; href=&quot;https://sauravtbpandey.blogspot.com/2026/10/the-ultimate-guide-to-environment.html&quot;&gt;read the alternative breakdown here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31504/stop-hardcoding-secrets-master-environment-variables-and-dotenv-in-node-js</guid>
<pubDate>Sat, 17 Oct 2026 08:03:00 +0000</pubDate>
</item>
<item>
<title>Why Express Applications Become Difficult to Secure at Scale</title>
<link>https://coderlegion.com/20268/why-express-applications-become-difficult-to-secure-at-scale</link>
<description>&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cslb5h4aldlzgmq78dsp.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;Express has earned its place as one of the most influential web frameworks in the Node.js ecosystem. Its simplicity, flexibility, and minimal learning curve have enabled countless startups and enterprises to build production-ready applications quickly. But as applications grow, teams often discover an uncomfortable reality: &lt;strong&gt;securing an Express application at scale becomes increasingly difficult.&lt;/strong&gt; This is not because Express is inherently insecure. In fact, Express intentionally stays minimal and gives developers total freedom to design systems however they choose. The real challenge is that security becomes a distributed responsibility—scattered across custom middleware, third-party packages, configuration files, team conventions, and individual developer discipline. As a system scales, this fragmented model inevitably starts to show its architectural limitations.&lt;/p&gt;
&lt;h3 id=&quot;the-security-complexity-problem&quot;&gt;The Security Complexity Problem&lt;/h3&gt;
&lt;p&gt;A small Express application might begin with a few clean routes and a handful of dependencies:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;app.get(&quot;/users&quot;, (req, res) =&amp;gt; {
    res.json(users);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this stage, everything feels manageable. Months later, however, the application evolves to meet production requirements, adding:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Authentication and Authorization logic&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Strict input validation and sanitization&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rate limiting and DDoS protection&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Complex CORS and CSRF configurations&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Session management and logging utilities&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reverse proxy integrations (Nginx, Cloudflare, etc.) Suddenly, every single request must travel through a dense, sequential chain of middleware before even reaching the core business logic:&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;app.use(cors());
app.use(helmet());
app.use(rateLimiter());
app.use(authMiddleware());
app.use(validationMiddleware());
app.use(customSecurityChecks());
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While nothing is inherently wrong with this middleware-driven approach, security is now distributed across multiple independent layers. This makes it incredibly difficult to reason about the overall security posture of the application. A single missing middleware registration or an incorrect execution order can silently create a critical vulnerability, accidentally allowing a route to bypass a vital protection layer.&lt;/p&gt;
&lt;h3 id=&quot;when-security-relies-on-team-convention&quot;&gt;When Security Relies on Team Convention&lt;/h3&gt;
&lt;p&gt;One of the most common pitfalls in large Express codebases is that security becomes convention-based rather than systemic. Engineering teams often rely on internal guidelines and code review checklists:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Always&lt;/em&gt; validate and sanitize inputs.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Always&lt;/em&gt; apply authentication guards to new routers.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Always&lt;/em&gt; implement specific security headers. The keyword here is &lt;strong&gt;always&lt;/strong&gt;. Security controls that depend entirely on human memory and rigorous discipline are inherently vulnerable to human error. As engineering teams grow and release cycles accelerate, enforcing these conventions consistently across dozens of files becomes nearly impossible. Express’s highly flexible architecture doesn't cause this problem, but its complete lack of guardrails tends to expose it more clearly than any other framework.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-dependency-explosion&quot;&gt;The Dependency Explosion&lt;/h3&gt;
&lt;p&gt;Another challenge emerges from the growing ecosystem of security-related third-party packages. Because Express provides no native security features, a typical production-grade application must pull in a vast array of dependencies: helmet, express-rate-limit, express-validator, cookie-parser, passport, and various session management libraries. Every single one of these external dependencies introduces:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Additional maintenance overhead:&lt;/strong&gt; Teams must constantly monitor, audit, and patch dependencies.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Supply-chain vulnerabilities:&lt;/strong&gt; A compromise in a deeply nested sub-dependency can expose the entire global runtime.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Configuration complexity:&lt;/strong&gt; Managing version compatibility and overlapping security assumptions across independently maintained packages becomes a shifting target. Security ceases to be a unified, cohesive system. Instead, it becomes an ecosystem of fragmented components where a breaking change in one package can silently disrupt security assumptions throughout the entire application.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-middleware-visibility-problem&quot;&gt;The Middleware Visibility Problem&lt;/h3&gt;
&lt;p&gt;While middleware chains are exceptionally powerful, they can easily mask security-critical behavior. Consider an endpoint protected by several layers:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;router.post(
    &quot;/admin/users&quot;,
    authenticate,
    authorize,
    validate,
    createUser
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At first glance, this declaration looks perfectly secure. However, the actual security guarantees are implicitly dependent on external factors: the exact execution order of the chain, the granular implementation details of each handler, how unhandled errors are passed down, and route registration consistency across separate router files. Understanding the true security model requires tracing request execution paths across multiple directories. As the codebase grows, this lack of centralized visibility drastically increases the probability of structural oversights.&lt;/p&gt;
&lt;h3 id=&quot;reactive-vs-proactive-architecture&quot;&gt;Reactive vs. Proactive Architecture&lt;/h3&gt;
&lt;p&gt;In many development pipelines, applications are built with functionality as the primary metric. Security features arrive progressively: The result of this workflow is a security model layered &lt;em&gt;on top&lt;/em&gt; of an existing architecture, rather than one integrated &lt;em&gt;into&lt;/em&gt; the foundation itself. This fosters a inherently reactive security culture. Engineering teams spend valuable time hunting for missing protections, auditing endpoints, and patching gaps, instead of relying on an infrastructure that guarantees safety by default.&lt;/p&gt;
&lt;h3 id=&quot;the-true-cost-of-human-error&quot;&gt;The True Cost of Human Error&lt;/h3&gt;
&lt;p&gt;Most devastating production security incidents are not caused by advanced attackers utilizing revolutionary zero-day exploits. They originate from simple, everyday mistakes made under tight deadlines:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;An unprotected endpoint forgotten during a major refactor.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A missing validation rule leading to a prototype pollution or NoSQL injection.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An exposed environment variable leaked via an accidental memory dump or supply-chain attack.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A route that accidentally bypasses the authentication middleware due to a typo in the router configuration. As a system scales, the probability of these minor oversights reaching production increases exponentially. The architectural question shifts from &lt;em&gt;&quot;Can developers make mistakes?&quot;&lt;/em&gt; to &lt;em&gt;&quot;How much structural damage can a single mistake cause?&quot;&lt;/em&gt; Modern security architecture must focus on reducing the impact radius of inevitable human error.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-rise-of-secure-by-default-framework-design&quot;&gt;The Rise of Secure-by-Default Framework Design&lt;/h3&gt;
&lt;p&gt;Faced with these scaling issues, the backend engineering community has begun questioning whether core security should heavily rely on developer discipline and middleware configuration. This has given rise to an alternative architectural philosophy: &lt;strong&gt;Security must be embedded directly into the framework core.&lt;/strong&gt; Instead of requiring developers to remember to import and configure every single protection layer for every new project, the runtime infrastructure provides sensible, strict security guarantees out of the box. This mirrors the principles used in cryptographic and defense-grade hardware design: safe defaults drastically minimize the attack surface before any custom application code even executes.&lt;/p&gt;
&lt;h4 id=&quot;shifting-the-paradigm-the-xypriss-approach&quot;&gt;Shifting the Paradigm: The XyPriss Approach&lt;/h4&gt;
&lt;p&gt;This vision is precisely what drives modern, secure-by-default runtime environments and frameworks like &lt;strong&gt;XyPriss&lt;/strong&gt;. Rather than treating security as an optional collection of external middleware, XyPriss moves critical protection layers straight into the framework core. By utilizing a hardened infrastructure, the runtime natively handles concerns that traditionally required separate packages and meticulous setup:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Native Request Normalization &amp;amp; Path Traversal Protection:&lt;/strong&gt; Preventing directory traversal and injection attempts at the routing level before they hit user code.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Anti-ReDoS Mechanisms:&lt;/strong&gt; Protecting the application from Regular Expression Denial of Service attacks natively.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Protected Environment Handling:&lt;/strong&gt; Isolating sensitive variables and preventing global scope leaks (like raw process.env exfiltration) via secure internal system gateways.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Zero-Trust Module Isolation:&lt;/strong&gt; Ensuring that third-party plugins and untrusted code cannot inherit root-level privileges or inspect sensitive memory states by default. The objective here is not to eliminate developer responsibility or restrict architectural freedom. The objective is to eliminate repetitive, high-stakes security decisions, ensuring that a simple human oversight cannot compromise the entire production ecosystem.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Express remains an exceptional, lightweight tool that powers a massive portion of the modern web. However, when scaling backend applications, complexity is the ultimate enemy of security. The bottleneck is rarely the framework itself; it is the management of an ever-expanding web of middleware, configurations, third-party packages, and team conventions. As backend systems become more complex and hostile, the industry is shifting toward architectures where security is not merely an administrative layer added post-deployment, but an immutable part of the framework's foundation. Because at scale, &lt;strong&gt;the safest security control is the one developers never have to remember to enable.&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id=&quot;further-reading&quot;&gt;Further Reading&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;XyPriss Security Overview:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://xypriss.nehonix.com/docs/security/overview&quot;&gt;https://xypriss.nehonix.com/docs/security/overview&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/20268/why-express-applications-become-difficult-to-secure-at-scale</guid>
<pubDate>Thu, 11 Jun 2026 09:14:39 +0000</pubDate>
</item>
<item>
<title>XyPriss: Rethinking Core Performance and Zero-Trust Architecture in Modern Backends</title>
<link>https://coderlegion.com/19576/xypriss-rethinking-core-performance-and-zero-trust-architecture-in-modern-backends</link>
<description>&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u288bouu3zofugf3hkdt.png&quot; alt=&quot;XyPriss banner - Built for Performance, Engineered for Security, showing architectural principles like Zero-Trust, Native Performance, and Modular design&quot;&gt;&lt;/p&gt;
&lt;p&gt;After two years of intensive development and architectural refinement, &lt;strong&gt;XyPriss&lt;/strong&gt; has officially reached full maturity. With the release of the &lt;strong&gt;v9.10.16++&lt;/strong&gt; stable branch, the framework is now 100% production-ready. &lt;/p&gt;
&lt;p&gt;We designed XyPriss from the ground up as a secure-by-default backend framework, powered by native internals and engineered specifically to sustain heavy, production-grade systems.&lt;/p&gt;
&lt;p&gt;The goal was never to build &lt;em&gt;&quot;just another framework&quot;&lt;/em&gt;. The goal was to solve critical infrastructure bottlenecks.&lt;/p&gt;
&lt;h4 id=&quot;core-architectural-pillars&quot;&gt;Core Architectural Pillars&lt;/h4&gt;
&lt;p&gt;XyPriss shifts the paradigm of modern backend development through four key principles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Zero-Trust Security:&lt;/strong&gt; Hardened by default. Security protocols and safe configurations are baked into the core, eliminating critical vulnerabilities before the first line of business logic is even written.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Native Performance:&lt;/strong&gt; Driven by a high-performance native engine designed for low-level execution speed, minimal memory overhead, and extreme portability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Modular by Design:&lt;/strong&gt; A highly flexible architecture that lets you scale components independently without introducing technical debt or breaking monolithic dependencies.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Production Reliability:&lt;/strong&gt; Rigorously tested to ensure operational stability under high-concurrency production workloads.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;battle-tested-and-growing&quot;&gt;Battle-Tested and Growing&lt;/h4&gt;
&lt;p&gt;What started as an ambitious technical challenge two years ago has evolved into a stable, powerful ecosystem. With the stability achieved in the current v9.10.16++ release cycle, XyPriss is ready for deployment in mission-critical environments.&lt;/p&gt;
&lt;p&gt;Whether you are optimizing transaction pipelines, building high-throughput APIs, or auditing system security, we invite you to explore what we have built.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;links-and-resources&quot;&gt;Links and Resources&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Documentation:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://xypriss.nehonix.com&quot;&gt;xypriss.nehonix.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Nehonix-Team/XyPriss&quot;&gt;Nehonix-Team/XyPriss&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;If you appreciate the work and engineering behind this framework, please take a second to star (like) our repository on GitHub. It costs nothing but it deeply encourages the team and gives the project the visibility it needs to grow.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Feel free to explore the repository, test the framework, or leave your technical feedback in the comments below.&lt;/em&gt; @typescripttutorials &lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/19576/xypriss-rethinking-core-performance-and-zero-trust-architecture-in-modern-backends</guid>
<pubDate>Wed, 03 Jun 2026 12:01:18 +0000</pubDate>
</item>
<item>
<title>How I built a Social Recipe Extractor that turns short-form video links into structured recipes</title>
<link>https://coderlegion.com/17735/built-social-recipe-extractor-that-turns-short-form-video-links-into-structured-recipes</link>
<description>&lt;p&gt;I recently built a feature for &lt;a rel=&quot;nofollow&quot; href=&quot;https://recipe-finder.org&quot;&gt;https://recipe-finder.org&lt;/a&gt; that takes a TikTok, Instagram, or YouTube cooking link and turns it into a structured recipe.&lt;/p&gt;
&lt;p&gt;The goal was simple: recipe inspiration lives on social platforms, but the actual cooking steps are usually buried inside captions, spoken instructions, or fast cuts. I wanted a workflow where a user pastes a link and gets back something usable: a recipe title, ingredient list, instructions, and a cleaner format for the rest of the product.&lt;/p&gt;
&lt;h4 id=&quot;the-flow&quot;&gt;The Flow&lt;/h4&gt;
&lt;p&gt;The core user journey is straightforward:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The user pastes a supported social video URL.&lt;/li&gt;
&lt;li&gt;The frontend sends that URL to a protected backend endpoint.&lt;/li&gt;
&lt;li&gt;The backend normalizes the link, fetches available source metadata and text, and runs an extraction pass.&lt;/li&gt;
&lt;li&gt;The result is converted into a structured recipe object.&lt;/li&gt;
&lt;li&gt;The app returns a clean recipe view, keeps recent imports in history, and can reuse cached results for repeated links.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;What makes this interesting isn't just the form input—it's the cleanup step between raw social content and something a user can actually cook from. &lt;/p&gt;
&lt;p&gt;Here is a look under the hood at how it's built using Vue 3, TypeScript, and Node.js/Express.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;1-frontend-entry-point&quot;&gt;1. Frontend Entry Point&lt;/h4&gt;
&lt;p&gt;The feature lives behind authentication and opens as a dedicated page in the app. The page itself stays simple and hands the main workflow to a dedicated &lt;code&gt;Social Recipe Extractor&lt;/code&gt; component. &lt;/p&gt;
&lt;p&gt;To keep the UI predictable and sleek (opting for a dark mode aesthetic with vibrant orange accents for active states), the component manages four main states:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The pasted social link&lt;/li&gt;
&lt;li&gt;Loading and error feedback&lt;/li&gt;
&lt;li&gt;The extracted recipe result&lt;/li&gt;
&lt;li&gt;Recent import history and usage state&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This keeps the input on the left, the result on the right, and prior imports available without mixing feature logic across multiple pages.&lt;/p&gt;
&lt;h4 id=&quot;2-backend-owned-extraction-flow&quot;&gt;2. Backend-Owned Extraction Flow&lt;/h4&gt;
&lt;p&gt;The frontend does not parse social content directly. Instead, it sends the link to a backend route dedicated to social recipe extraction. &lt;/p&gt;
&lt;p&gt;That backend layer is responsible for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Validating the request&lt;/li&gt;
&lt;li&gt;Normalizing the URL into a canonical form&lt;/li&gt;
&lt;li&gt;Checking whether a usable cached import already exists&lt;/li&gt;
&lt;li&gt;Fetching source metadata and extracted text from the target platform&lt;/li&gt;
&lt;li&gt;Transforming unstructured content into a recipe-shaped response&lt;/li&gt;
&lt;li&gt;Returning a stable payload the frontend can render safely&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is the right ownership boundary because platform parsing, rate control, caching, and extraction quality all belong on the server. The frontend only ever deals with a typed response.&lt;/p&gt;
&lt;h4 id=&quot;3-structured-extraction&quot;&gt;3. Structured Extraction&lt;/h4&gt;
&lt;p&gt;The most important design choice was ensuring the pipeline doesn't stop at &quot;grab some text from the page.&quot; It pushes the output into a highly structured shape:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Recipe title&lt;/li&gt;
&lt;li&gt;Prep time&lt;/li&gt;
&lt;li&gt;Ingredients&lt;/li&gt;
&lt;li&gt;Instructions&lt;/li&gt;
&lt;li&gt;Normalized ingredients&lt;/li&gt;
&lt;li&gt;Source metadata (platform, thumbnail, title, author)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This matters because a structured result is much easier to reuse than a raw text dump. It allows the data to power future features without rebuilding the extraction logic each time.&lt;/p&gt;
&lt;h4 id=&quot;4-normalization-and-cleanup&quot;&gt;4. Normalization and Cleanup&lt;/h4&gt;
&lt;p&gt;Social content is inherently messy. Titles contain hashtags, descriptions are noisy, and ingredient mentions are often incomplete or informal. &lt;/p&gt;
&lt;p&gt;The implementation handles this by cleaning and normalizing content before &lt;em&gt;and&lt;/em&gt; after extraction:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;URL normalization&lt;/strong&gt; ensures equivalent links map to the same import record.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Title cleanup&lt;/strong&gt; prevents social-platform suffixes and noisy text from leaking into the recipe title.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Whitespace and escaped-text cleanup&lt;/strong&gt; is applied before extraction.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ingredient normalization&lt;/strong&gt; makes the output more consistent for downstream product use.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This cleanup layer is where a lot of the actual product quality is realized. &lt;/p&gt;
&lt;h4 id=&quot;5-caching-and-history&quot;&gt;5. Caching and History&lt;/h4&gt;
&lt;p&gt;Two product decisions make the feature feel significantly faster and more useful:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Cached imports&lt;/strong&gt; prevent unnecessary repeat processing for the same normalized link.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Import history&lt;/strong&gt; gives users a lightweight library of recently extracted recipes.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The extractor isn't just a one-shot tool; it becomes a reusable workflow inside the product.&lt;/p&gt;
&lt;h4 id=&quot;6-premium-friendly-usage-model&quot;&gt;6. Premium-Friendly Usage Model&lt;/h4&gt;
&lt;p&gt;Finally, the feature returns usage information along with the extraction result. That allows the UI to immediately show whether the user can continue importing, how much of their current allowance is used, and when an upgrade prompt should appear. &lt;/p&gt;
&lt;p&gt;This is a much better user experience than hard-failing late in the flow, as the interface can explain the state before the user hits a dead end.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h4&gt;
&lt;p&gt;This is one of those features where the product value comes purely from reducing friction. People already save recipes from social media. The real improvement is turning that saved link into something searchable, reusable, and easier to cook from. &lt;/p&gt;
&lt;p&gt;If I were extending it further, the next logical step would be connecting the extracted, structured recipe directly into grocery list generation, smart meal planning, and nutrition analysis.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/17735/built-social-recipe-extractor-that-turns-short-form-video-links-into-structured-recipes</guid>
<pubDate>Wed, 13 May 2026 15:44:38 +0000</pubDate>
</item>
<item>
<title>From Modal to Full Page: How We Refactored a Vue 3 Recipe Detail View (and Made It Faster)</title>
<link>https://coderlegion.com/16538/from-modal-to-full-page-how-we-refactored-a-vue-3-recipe-detail-view-and-made-it-faster</link>
<description>&lt;h1&gt;From Modal to Full Page: How We Refactored a Vue 3 Recipe Detail View (and Made It Faster)&lt;/h1&gt;
&lt;p&gt;Demo:&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://recipe-finder.org/recipe/644488-german-rhubarb-cake-with-meringue&quot;&gt;https://recipe-finder.org/recipe/644488-german-rhubarb-cake-with-meringue&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;One of the longest-lived technical decisions in our recipe finder app was showing recipe details inside a dialog modal. It worked — until it didn't. Here's how we migrated from a bloated modal to a clean, SEO-friendly full page, what we cut along the way, and what the app looks like now.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The Old Approach: Everything in a Modal&lt;/h2&gt;
&lt;p&gt;The original setup opened a &lt;code&gt;&amp;lt;v-dialog&amp;gt;&lt;/code&gt; when a user clicked a recipe card. The modal held the entire recipe detail UI: ingredients, nutrition, videos, AI chef, grocery import, recipe scaler — all of it. The logic for opening it, fetching the recipe, and handling deep-link slugs lived inside &lt;code&gt;HomePage.vue&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Old: HomePage.vue controlled everything --&amp;gt;
&amp;lt;RecipeDetailsModal
  :is-open=&quot;isRecipeModalOpen&quot;
  :recipe=&quot;selectedRecipeDetails&quot;
  :loading=&quot;loadingRecipeDetails&quot;
  @close=&quot;closeModal&quot;
/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;The problem was that &lt;code&gt;HomePage.vue&lt;/code&gt; had become a god component. It managed:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The search form and results&lt;/li&gt;
  &lt;li&gt;Cuisine carousel&lt;/li&gt;
  &lt;li&gt;Recipe of the day&lt;/li&gt;
  &lt;li&gt;Recent recipes&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;And&lt;/strong&gt; the modal open/close state, slug parsing, and detail fetch&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On top of that, because everything was in a modal, the URL never changed. Users couldn't share a link to a specific recipe, Google couldn't index the content, and the Back button did nothing useful.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The New Approach: Dedicated Route + Page&lt;/h2&gt;
&lt;p&gt;We created &lt;code&gt;/recipe/:slug&lt;/code&gt; as a proper route and moved the recipe detail logic into a standalone &lt;code&gt;RecipeDetailPage.vue&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// router/index.ts
{
  path: '/recipe/:slug',
  component: () =&amp;gt; import('@/pages/RecipeDetailPage.vue'),
  meta: { title: 'Recipe' }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Slugs are derived from the recipe ID and title, making them human-readable and stable:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// utils/index.ts
export const toRecipeSlug = (id: number, title: string): string =&amp;gt; {
  const slug = title
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/(^-|-$)/g, '');
  return `${id}-${slug}`;
};

export const extractRecipeIdFromSlug = (slug: string): number | null =&amp;gt; {
  const id = parseInt(slug.split('-')[0]);
  return isNaN(id) ? null : id;
};&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;What We Cut From HomePage.vue&lt;/h2&gt;
&lt;p&gt;Once the page was independent, we stripped &lt;code&gt;HomePage.vue&lt;/code&gt; of everything modal-related. Gone:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code&gt;isRecipeModalOpen&lt;/code&gt; ref&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;selectedRecipeDetails&lt;/code&gt; and &lt;code&gt;loadingRecipeDetails&lt;/code&gt; state&lt;/li&gt;
  &lt;li&gt;The &lt;code&gt;openRecipeModal&lt;/code&gt; / &lt;code&gt;closeModal&lt;/code&gt; handlers&lt;/li&gt;
  &lt;li&gt;The slug-watching &lt;code&gt;watch&lt;/code&gt; that re-fetched on URL change&lt;/li&gt;
  &lt;li&gt;The &lt;code&gt;&amp;lt;RecipeDetailsModal&amp;gt;&lt;/code&gt; import and component registration&lt;/li&gt;
  &lt;li&gt;The &lt;code&gt;handleOpenRecipeDetails&lt;/code&gt; function passed down through three component layers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result was &lt;code&gt;HomePage.vue&lt;/code&gt; shrinking by roughly &lt;strong&gt;40%&lt;/strong&gt; in script size. It now does one thing: show the search form and results.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;What the Page Layout Looks Like&lt;/h2&gt;
&lt;p&gt;The page uses a standard Vuetify two-column grid — main content on the left, sticky sidebar on the right (desktop only). On mobile, the sidebar collapses and the tools surface inline.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;v-row&amp;gt;
  &amp;lt;!-- Left: main content --&amp;gt;
  &amp;lt;v-col cols=&quot;12&quot; lg=&quot;8&quot;&amp;gt;
    &amp;lt;v-img :src=&quot;recipe.image&quot; cover rounded=&quot;lg&quot; class=&quot;mb-6&quot; /&amp;gt;
    &amp;lt;h1 class=&quot;recipe-title&quot;&amp;gt;{{ recipe.title }}&amp;lt;/h1&amp;gt;
    &amp;lt;!-- chips, rating, action buttons, summary, ingredients, instructions, nutrition --&amp;gt;
  &amp;lt;/v-col&amp;gt;

  &amp;lt;!-- Right: sticky sidebar (desktop only) --&amp;gt;
  &amp;lt;v-col cols=&quot;12&quot; lg=&quot;4&quot; class=&quot;d-none d-lg-flex flex-column&quot;&amp;gt;
    &amp;lt;div class=&quot;sidebar-sticky&quot;&amp;gt;
      &amp;lt;!-- Recipe Tools card --&amp;gt;
      &amp;lt;!-- AI Cooking Chef card --&amp;gt;
      &amp;lt;!-- Nutrition Snapshot card --&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/v-col&amp;gt;
&amp;lt;/v-row&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The sidebar cards use a glass morphism style that matches the rest of the app:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.glass-card {
  border: 1px solid rgba(255, 255, 255, 0.1);
  background:
    radial-gradient(circle at top right, rgba(255, 163, 92, 0.08), transparent 40%),
    linear-gradient(165deg, rgba(255, 255, 255, 0.07), rgba(255, 255, 255, 0.02));
  border-radius: 16px !important;
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;Mobile: Icon Buttons + Bottom Sheet AI&lt;/h2&gt;
&lt;p&gt;On mobile, the page can't show a sidebar. We solved this in two ways:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Action buttons&lt;/strong&gt; become icon-only on small screens, matching how the modal used to look:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Mobile: icon-only --&amp;gt;
&amp;lt;div class=&quot;d-flex d-sm-none gap-2 mb-6 align-center&quot;&amp;gt;
  &amp;lt;v-btn icon variant=&quot;tonal&quot; size=&quot;small&quot; @click.stop=&quot;handleToggleFavoriteRecipe&quot;&amp;gt;
    &amp;lt;v-icon&amp;gt;{{ isFavorited ? 'mdi-heart' : 'mdi-heart-outline' }}&amp;lt;/v-icon&amp;gt;
  &amp;lt;/v-btn&amp;gt;
  &amp;lt;v-btn icon variant=&quot;tonal&quot; size=&quot;small&quot; @click=&quot;shareRecipe&quot;&amp;gt;
    &amp;lt;v-icon&amp;gt;{{ isCopied ? 'mdi-check' : 'mdi-share-variant' }}&amp;lt;/v-icon&amp;gt;
  &amp;lt;/v-btn&amp;gt;
  &amp;lt;v-btn icon variant=&quot;tonal&quot; size=&quot;small&quot; @click=&quot;printRecipe&quot;&amp;gt;
    &amp;lt;v-icon&amp;gt;mdi-printer&amp;lt;/v-icon&amp;gt;
  &amp;lt;/v-btn&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;!-- Desktop: text buttons --&amp;gt;
&amp;lt;div class=&quot;d-none d-sm-flex flex-wrap gap-2 mb-6&quot;&amp;gt;
  &amp;lt;v-btn prepend-icon=&quot;mdi-heart-outline&quot; variant=&quot;tonal&quot; class=&quot;page-action-btn&quot;&amp;gt;
    Add to Favorites
  &amp;lt;/v-btn&amp;gt;
  &amp;lt;!-- ... --&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;The AI chef&lt;/strong&gt; becomes a bottom sheet triggered by a text button inline with the ingredient tools (no floating action button cluttering the screen):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Ingredient row: Scale / Analyze / Grocery / AI Chef --&amp;gt;
&amp;lt;v-btn color=&quot;primary&quot; variant=&quot;text&quot; size=&quot;small&quot;
  prepend-icon=&quot;mdi-robot-excited&quot; @click=&quot;showAiSheet = true&quot;&amp;gt;
  AI Chef
&amp;lt;/v-btn&amp;gt;

&amp;lt;v-bottom-sheet v-model=&quot;showAiSheet&quot;&amp;gt;
  &amp;lt;!-- full AI interface --&amp;gt;
&amp;lt;/v-bottom-sheet&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;SEO + Meta Tags&lt;/h2&gt;
&lt;p&gt;Since the content is now on a real URL, we inject &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; and Open Graph meta tags dynamically on load:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const injectMetaTags = (title: string, summary: string, imageUrl: string) =&amp;gt; {
  const pageTitle = `${title} | Recipe Finder`;
  document.title = pageTitle;

  const setMeta = (selector: string, attr: string, value: string) =&amp;gt; {
    document.querySelector(selector)?.setAttribute(attr, value);
  };

  setMeta(&quot;meta[property='og:title']&quot;, 'content', pageTitle);
  setMeta(&quot;meta[name='twitter:title']&quot;, 'content', pageTitle);

  if (summary) {
    const clean = summary.replace(/&amp;lt;[^&amp;gt;]*&amp;gt;/g, '').trim().slice(0, 155);
    setMeta(&quot;meta[name='description']&quot;, 'content', clean);
    setMeta(&quot;meta[property='og:description']&quot;, 'content', clean);
  }

  if (imageUrl) {
    setMeta(&quot;meta[property='og:image']&quot;, 'content', imageUrl);
  }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is called inside a &lt;code&gt;watch&lt;/code&gt; on the recipe computed ref, so it fires on both initial load and when navigating between similar recipes.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Async Components Throughout&lt;/h2&gt;
&lt;p&gt;Every non-critical UI piece is lazy-loaded:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const ImportToGroceryList = defineAsyncComponent(
  () =&amp;gt; import('@/components/ImportToGroceryList.vue')
);
const PremiumUpgradeDialog = defineAsyncComponent(
  () =&amp;gt; import('@/components/PremiumUpgradeDialog.vue')
);
const RecipeScaler = defineAsyncComponent(
  () =&amp;gt; import('@/components/RecipeScaler.vue')
);
const RecipeEmbedWatermark = defineAsyncComponent(
  () =&amp;gt; import('@/components/RecipeEmbedWatermark.vue')
);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The main recipe content renders immediately. The grocery dialog, scaler, embed widget, and upgrade dialog only load if the user actually interacts with them.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Auth Guard on Grocery Import&lt;/h2&gt;
&lt;p&gt;One regression we caught: clicking &quot;Send to Grocery List&quot; while logged out was calling &lt;code&gt;getAccessTokenSilently()&lt;/code&gt; and throwing an Auth0 missing refresh token error. Fixed by checking auth state before opening the dialog:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const onDialogToggle = (open: boolean) =&amp;gt; {
  if (open) {
    if (!isAuthenticated.value) {
      dialogOpen.value = false;
      loginWithRedirect();
      return;
    }
    loadGroceryLists();
  }
};&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;The Result&lt;/h2&gt;
&lt;p&gt;HomePage.vue Script Size: Reduced from ~650 lines to ~390 lines&lt;/p&gt;
&lt;p&gt;Recipe URL: Now fully shareable&lt;/p&gt;
&lt;p&gt;Google Indexing: Now fully indexable&lt;/p&gt;
&lt;p&gt;Mobile Layout: Upgraded from a fullscreen modal to a native page with a bottom sheet&lt;/p&gt;
&lt;p&gt;Auth Crash on Grocery: Fixed&lt;/p&gt;
&lt;p&gt;AI on Mobile: Moved from a floating button to an inline trigger&lt;br&gt;
&lt;/p&gt;&lt;p&gt;The modal still exists for the recipe list view (quick-peek without leaving the page), but the canonical experience is now a proper page. The code is cleaner, the app is faster to load, and every recipe finally has a real URL.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Built with Vue 3, Vuetify 3, TypeScript, and Tailwind CSS. Auth via Auth0.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/16538/from-modal-to-full-page-how-we-refactored-a-vue-3-recipe-detail-view-and-made-it-faster</guid>
<pubDate>Sun, 03 May 2026 19:43:07 +0000</pubDate>
</item>
<item>
<title>Built a Laravel-Inspired Framework in Node.js (Here’s Why) - NetPress</title>
<link>https://coderlegion.com/15978/built-a-laravel-inspired-framework-in-node-js-heres-why-netpress</link>
<description>&lt;p&gt;For years, I’ve been working with Laravel.&lt;/p&gt;
&lt;p&gt;It gave me something most Node.js ecosystems don’t:&lt;br&gt;
structure, clarity, and speed of development.&lt;/p&gt;
&lt;p&gt;But every time I switched to Node.js, I felt like I was rebuilding the same things:&lt;/p&gt;
&lt;p&gt;routing structure&lt;br&gt;
middleware flow&lt;br&gt;
validation&lt;br&gt;
auth patterns&lt;br&gt;
project organization&lt;/p&gt;
&lt;p&gt;Everything felt… fragmented.&lt;/p&gt;
&lt;p&gt;So I decided to build my own solution.&lt;/p&gt;
&lt;p&gt;The Problem&lt;/p&gt;
&lt;p&gt;Node.js is powerful, but:&lt;/p&gt;
&lt;p&gt;too many choices&lt;br&gt;
no clear “standard” architecture&lt;br&gt;
a lot of boilerplate&lt;br&gt;
every project looks different&lt;/p&gt;
&lt;p&gt;Compare that to Laravel:&lt;/p&gt;
&lt;p&gt;consistent structure&lt;br&gt;
batteries included&lt;br&gt;
clean developer experience&lt;/p&gt;
&lt;p&gt;That’s what I wanted in Node.&lt;/p&gt;
&lt;p&gt;The Idea&lt;/p&gt;
&lt;p&gt;Build a framework that feels like Laravel…&lt;br&gt;
but runs on Node.js.&lt;/p&gt;
&lt;p&gt;That’s how Netpress started.&lt;/p&gt;
&lt;p&gt;What I Focused On&lt;/p&gt;
&lt;p&gt;Instead of building “just another framework”, I focused on:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Predictable Structure&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Every project follows a clear pattern:&lt;/p&gt;
&lt;p&gt;Controllers&lt;br&gt;
Middleware&lt;br&gt;
Services&lt;br&gt;
Models&lt;/p&gt;
&lt;p&gt;No chaos. No guessing.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Clean Routing&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Grouping, middleware, prefixes… just like Laravel:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;router.group(authMiddleware, (router) =&amp;gt; {
  router.get('/me', meHandler);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Middleware Pipeline&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Simple and powerful request flow:&lt;/p&gt;
&lt;p&gt;auth&lt;br&gt;
rate limiting&lt;br&gt;
logging&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Rendering Support&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can use:&lt;/p&gt;
&lt;p&gt;Vue&lt;br&gt;
React&lt;/p&gt;
&lt;p&gt;Server-driven or hybrid apps without overcomplication.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Developer Experience&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Commands, structure, and conventions that make you move fast:&lt;/p&gt;
&lt;p&gt;generate app&lt;br&gt;
setup rendering&lt;br&gt;
run migrations&lt;br&gt;
seed data&lt;br&gt;
Why Not Just Use Express?&lt;/p&gt;
&lt;p&gt;You can.&lt;/p&gt;
&lt;p&gt;But then you’ll rebuild:&lt;/p&gt;
&lt;p&gt;structure&lt;br&gt;
conventions&lt;br&gt;
patterns&lt;br&gt;
architecture decisions&lt;/p&gt;
&lt;p&gt;Again… and again.&lt;/p&gt;
&lt;p&gt;Netpress tries to remove that friction.&lt;/p&gt;
&lt;p&gt;A Real Example&lt;/p&gt;
&lt;p&gt;Instead of wiring everything manually:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;app.use(authMiddleware);
app.get('/me', handler);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You get a structured flow:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;router.group(authMiddleware, (router) =&amp;gt; {
  router.get('/me', meHandler);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What I Learned&lt;/p&gt;
&lt;p&gt;Building this taught me:&lt;/p&gt;
&lt;p&gt;developers don’t just need tools, they need opinionated structure&lt;br&gt;
consistency beats flexibility in most real-world apps&lt;br&gt;
good DX is underrated&lt;/p&gt;
&lt;p&gt;Here’s the docs:&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://admicaa.github.io/netpress-docs/&quot;&gt;https://admicaa.github.io/netpress-docs/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Would love to hear your thoughts.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/15978/built-a-laravel-inspired-framework-in-node-js-heres-why-netpress</guid>
<pubDate>Mon, 27 Apr 2026 14:30:01 +0000</pubDate>
</item>
<item>
<title>Error [err_http_headers_sent]: cannot set headers after they are sent to the client</title>
<link>https://coderlegion.com/13668/error-errhttpheaderssent-cannot-set-headers-after-they-are-sent-to-the-client</link>
<description>&lt;div style=&quot;background-color:#f0f8ff; padding:10px; border-left:5px solid #0078D7; line-height:1.5;&quot;&gt;
 **Before you dive into this article...**&lt;br&gt;
 Check out our vibrant new community at &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com&quot; style=&quot;color:#0078D7; font-weight:bold;&quot;&gt;CoderLegion.com&lt;/a&gt;!&lt;br&gt;
 Share your knowledge, connect with like-minded developers, and grow together.&lt;br&gt;
 &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com&quot; style=&quot;color:#ff4500; font-weight:bold;&quot;&gt;Click here to join now!&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Problem :&lt;br&gt;
I am very new to the Node.js and I am having few issues. I am using the Node.js 4.10 and Express 2.4.3. While trying to execute my code I am receiving the below error : Error: Can't render headers after they are sent to the client.&lt;/p&gt;
&lt;p&gt;at ServerResponse. (http.js:580:12)&lt;br&gt;
at ServerResponse._renderHeaders (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:70:30)&lt;br&gt;
at ServerResponse.writeHead (http.js:830:30)&lt;br&gt;
at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/auth.strategies/facebook.js:30:20&lt;br&gt;
at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/index.js:120:20&lt;br&gt;
at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/strategyExecutor.js:50:40)&lt;br&gt;
at [object Object].pass (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/authExecutionScope.js:29:6)&lt;br&gt;
at [object Object].halt (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/authExecutionScope.js:35:9)&lt;br&gt;
at [object Object].redirect (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/authExecutionScope.js:20:10)&lt;br&gt;
at [object Object]. (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/auth.strategies/facebook.js:80:21)&lt;br&gt;
Error: Can't set headers after they are sent.&lt;br&gt;
at ServerResponse. (http.js:540:22)&lt;br&gt;
at ServerResponse.setHeader (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:60:30)&lt;br&gt;
at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:170:23)&lt;br&gt;
at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:201:15)&lt;br&gt;
at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:170:30)&lt;br&gt;
at param (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:195:17)&lt;br&gt;
at pass (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:202:16)&lt;br&gt;
at Object.router [as handle] (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:209:10)&lt;br&gt;
at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:204:20)&lt;br&gt;
at Object.auth [as handle] (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/index.js:156:9)&lt;br&gt;
Error: Can't set headers after they are sent.&lt;br&gt;
.&lt;br&gt;
.&lt;br&gt;
.&lt;/p&gt;
&lt;p&gt;My code as below :&lt;/p&gt;
&lt;p&gt;var realfbId= &quot;XXX&quot;;&lt;br&gt;
var realfbSecret= &quot;XXXXXX&quot;;&lt;br&gt;
var realfbCallbackAddress= &quot;&lt;a rel=&quot;nofollow&quot; href=&quot;http://127.0.0.1:8888/auth/facebook_callback&quot;&gt;http://127.0.0.1:8888/auth/facebook_callback&lt;/a&gt;&quot;&lt;br&gt;
var realcookieSecret = &quot;node&quot;;     //please  enter a random hash for security&lt;br&gt;
var realexpress= require('realexpress');&lt;br&gt;
var realauth = require('connect-auth')&lt;br&gt;
var realapp = realexpress.createServer();&lt;br&gt;
realapp.configure(function(){&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;realapp.use(realexpress.bodyParser());
realapp.use(realexpress.methodOverride());
realapp.use(realexpress.cookieParser());
realapp.use(realexpress.session({secret: cookieSecret}));
realapp.use(realauth([
    realauth.Facebook({
        appId : realfbId,
       appSecret: realfbSecret,
        callback: realfbCallbackAddress,
        scope: 'offline_access,email,user_about_me,user_activities,manage_pages,publish_stream',
        failedUri: '/noauth'
    })
]));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;   realapp.use(realapp.router);&lt;br&gt;
});&lt;br&gt;
.&lt;br&gt;
.&lt;br&gt;
.&lt;/p&gt;
&lt;p&gt;Solution :&lt;br&gt;
If you are using the callback functions then use the return after the err block. This is one of the most frequent scenarios in which this error can happen.&lt;/p&gt;
&lt;p&gt;realuserModel.createUser(data, function(realerr, realdata) {&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if(realerr) {

  res.status = 422

  res.json(realerr)

  return // without this return the error can happen.

}

return res.json(realdata)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  })&lt;br&gt;
I had Tested it on Node version v10.16.0 and express 4.16.4&lt;/p&gt;
&lt;p&gt;Another Solution:&lt;br&gt;
The res object in Express is a subclass of Node.js's http.ServerResponse (read the http.js source). You are allowed to call res.setHeader(name, value) as often as you want until you call res.writeHead(statusCode). After writeHead, the headers are baked in and you can only call res.write(data), and finally res.end(data).&lt;/p&gt;
&lt;p&gt;The error &quot;Error: Can't set headers after they are sent.&quot; means that you're already in the Body or Finished state, but some function tried to set a header or statusCode. When you see this error, try to look for anything that tries to send a header after some of the body has already been written. For example, look for callbacks that are accidentally called twice, or any error that happens after the body is sent.&lt;/p&gt;
&lt;p&gt;In your case, you called res.redirect(), which caused the response to become Finished. Then your code threw an error (res.req is null). and since the error happened within your actual function(req, res, next) (not within a callback), Connect was able to catch it and then tried to send a 500 error page. But since the headers were already sent, Node.js's setHeader threw the error that you saw.&lt;/p&gt;
&lt;p&gt;Good Middleware&lt;br&gt;
Code:&lt;/p&gt;
&lt;p&gt;// middleware that does not modify the response body&lt;br&gt;
var doesNotModifyBody = function(request, response, next) {&lt;br&gt;
  request.params = {&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a: &quot;b&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  };&lt;br&gt;
  // calls next because it hasn't modified the header&lt;br&gt;
  next();&lt;br&gt;
};&lt;/p&gt;
&lt;p&gt;// middleware that modify the response body&lt;br&gt;
var doesModifyBody = function(request, response, next) {&lt;br&gt;
  response.setHeader(&quot;Content-Type&quot;, &quot;text/html&quot;);&lt;br&gt;
  response.write(&quot;&lt;/p&gt;&lt;p&gt;Hello World&lt;/p&gt;&quot;);&lt;br&gt;
  response.end();&lt;br&gt;
  // doesn't call next()&lt;br&gt;
};
&lt;p&gt;app.use(doesNotModifyBody);&lt;br&gt;
app.use(doesModifyBody);&lt;/p&gt;
&lt;p&gt;Problematic Middleware&lt;/p&gt;
&lt;p&gt;var problemMiddleware = function(request, response, next) {&lt;br&gt;
  response.setHeader(&quot;Content-Type&quot;, &quot;text/html&quot;);&lt;br&gt;
  response.write(&quot;&lt;/p&gt;&lt;p&gt;Hello World&lt;/p&gt;&quot;);&lt;br&gt;
  next();&lt;br&gt;
};&lt;br&gt;
The problematic middleware sets the response header without calling response.end() and calls next(), which confuses connect's server.
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/13668/error-errhttpheaderssent-cannot-set-headers-after-they-are-sent-to-the-client</guid>
<pubDate>Mon, 30 Mar 2026 04:06:22 +0000</pubDate>
</item>
<item>
<title>Build a Personal Blog with Node.js and Express</title>
<link>https://coderlegion.com/11904/build-a-personal-blog-with-node-js-and-express</link>
<description>&lt;p&gt;Creating a personal blog is a great way to share your thoughts, ideas, and projects with the world. In this tutorial, we'll walk through building and deploying a blog application using Node.js and Express.&lt;/p&gt;
&lt;h3 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h3&gt;
&lt;p&gt;Before starting, ensure you have the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Node.js installed on your machine&lt;/li&gt;
&lt;li&gt;Basic understanding of JavaScript&lt;/li&gt;
&lt;li&gt;A code editor (like VS Code)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;step-1-set-up-your-project&quot;&gt;Step 1: Set Up Your Project&lt;/h3&gt;
&lt;p&gt;Open your terminal and create a new directory for your blog. Navigate to it and initialize a new Node.js project:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mkdir my-personal-blog
cd my-personal-blog
npm init -y
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This creates a &lt;code&gt;package.json&lt;/code&gt; file, where all your project dependencies will reside.&lt;/p&gt;
&lt;h3 id=&quot;step-2-install-necessary-packages&quot;&gt;Step 2: Install Necessary Packages&lt;/h3&gt;
&lt;p&gt;Next, you’ll need to install Express and some other helpful packages:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install express body-parser ejs mongoose dotenv
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;express&lt;/strong&gt; handles your server logic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;body-parser&lt;/strong&gt; helps parse incoming request bodies.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ejs&lt;/strong&gt; is a templating engine for rendering HTML.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;mongoose&lt;/strong&gt; interacts with MongoDB for data management.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;dotenv&lt;/strong&gt; manages environment variables.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;step-3-set-up-your-server&quot;&gt;Step 3: Set Up Your Server&lt;/h3&gt;
&lt;p&gt;Create a new file called &lt;code&gt;server.js&lt;/code&gt; and set up a basic Express server:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const app = express();
const PORT = process.env.PORT || 5000;

mongoose.connect('mongodb://localhost/my-blog', { useNewUrlParser: true, useUnifiedTopology: true });

app.use(bodyParser.urlencoded({ extended: true }));
app.set('view engine', 'ejs');

app.get('/', (req, res) =&amp;gt; {
  res.send('Welcome to My Personal Blog');
});

app.listen(PORT, () =&amp;gt; {
  console.log(`Server is running on port ${PORT}`);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This simple setup initiates an Express server that listens for requests.&lt;/p&gt;
&lt;h3 id=&quot;step-4-define-your-database-schema&quot;&gt;Step 4: Define Your Database Schema&lt;/h3&gt;
&lt;p&gt;Create a folder named &lt;code&gt;models&lt;/code&gt; and add a file named &lt;code&gt;Post.js&lt;/code&gt; for your blog posts schema:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const mongoose = require('mongoose');

const postSchema = new mongoose.Schema({
  title: String,
  content: String,
  createdAt: { type: Date, default: Date.now }
});

module.exports = mongoose.model('Post', postSchema);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;step-5-create-blog-routes&quot;&gt;Step 5: Create Blog Routes&lt;/h3&gt;
&lt;p&gt;Add routes to create and display posts. In your &lt;code&gt;server.js&lt;/code&gt;, include the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const Post = require('./models/Post'); 

app.get('/posts', async (req, res) =&amp;gt; {
  const posts = await Post.find();
  res.render('posts', { posts });
});

app.post('/posts', async (req, res) =&amp;gt; {
  const newPost = new Post({ title: req.body.title, content: req.body.content });
  await newPost.save();
  res.redirect('/posts');
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;step-6-create-views&quot;&gt;Step 6: Create Views&lt;/h3&gt;
&lt;p&gt;Create a &lt;code&gt;views&lt;/code&gt; folder, and within it create &lt;code&gt;posts.ejs&lt;/code&gt; which renders your posts:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;title&amp;gt;My Blog&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;h1&amp;gt;My Blog Posts&amp;lt;/h1&amp;gt;
  &amp;lt;form action=&quot;/posts&quot; method=&quot;POST&quot;&amp;gt;
    &amp;lt;input type=&quot;text&quot; name=&quot;title&quot; placeholder=&quot;Title&quot; required&amp;gt;
    &amp;lt;textarea name=&quot;content&quot; placeholder=&quot;Content&quot; required&amp;gt;&amp;lt;/textarea&amp;gt;
    &amp;lt;button type=&quot;submit&quot;&amp;gt;Add Post&amp;lt;/button&amp;gt;
  &amp;lt;/form&amp;gt;
  &amp;lt;ul&amp;gt;
    &amp;lt;% posts.forEach(post =&amp;gt; { %&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;h2&amp;gt;&amp;lt;%= post.title %&amp;gt;&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;&amp;lt;%= post.content %&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;
    &amp;lt;% }) %&amp;gt;
  &amp;lt;/ul&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;step-7-test-your-application&quot;&gt;Step 7: Test Your Application&lt;/h3&gt;
&lt;p&gt;Start your server:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;node server.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open your browser and navigate to &lt;code&gt;http://localhost:5000&lt;/code&gt;. You should see your personal blog!&lt;/p&gt;
&lt;h3 id=&quot;step-8-deploy-your-blog&quot;&gt;Step 8: Deploy Your Blog&lt;/h3&gt;
&lt;p&gt;Now that your blog is up and running, consider deploying it using platforms like Heroku or Vercel. Follow their guides on how to deploy Node.js applications.&lt;/p&gt;
&lt;h3 id=&quot;conclusion-1&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Congratulations! You’ve built and deployed a personal blog using Node.js and Express. This project sets a solid foundation for exploring more advanced features like user authentication, comments, or even media uploads in the future. Happy coding!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/11904/build-a-personal-blog-with-node-js-and-express</guid>
<pubDate>Thu, 19 Feb 2026 08:29:45 +0000</pubDate>
</item>
<item>
<title>Building Web Servers with Node.js and Bun – A Backend Developer’s First Step</title>
<link>https://coderlegion.com/4293/building-web-servers-with-node-js-and-bun-a-backend-developers-first-step</link>
<description>&lt;h2 id=&quot;welcome-back-to-the-backend-development-series-&quot;&gt;Welcome Back to the Backend Development Series!&lt;/h2&gt;
&lt;p&gt;In the &lt;strong&gt;previous post&lt;/strong&gt;, we laid the groundwork:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;✅ We explained what a &lt;strong&gt;web server&lt;/strong&gt; is&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ We explored the &lt;strong&gt;Client-Server Architecture&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ We saw how &lt;strong&gt;JavaScript can run on the backend&lt;/strong&gt; using &lt;strong&gt;Node.js&lt;/strong&gt; and &lt;strong&gt;Bun&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ And most importantly, we &lt;strong&gt;built our very first web server&lt;/strong&gt; using Node’s built-in &lt;code&gt;http&lt;/code&gt; module&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But that server had one big limitation...&lt;/p&gt;
&lt;h3 id=&quot;-the-problem-with-our-first-server&quot;&gt;⚠️ The Problem With Our First Server&lt;/h3&gt;
&lt;p&gt;In our first server, no matter what URL you visited — whether it was &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;/about&lt;/code&gt;, &lt;code&gt;/ice-tea&lt;/code&gt;, or even &lt;code&gt;/random-nonsense&lt;/code&gt; — the server would respond with the &lt;strong&gt;exact same message&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;Hello from your first Node server!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why? Because we didn’t check what URL the user was actually requesting.&lt;br&gt;
We just responded blindly to &lt;strong&gt;every single request&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;And that’s not how real-world websites work, right?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;/login&lt;/code&gt; should show a login form&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;/about&lt;/code&gt; should show info about your site&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;/ice-tea&lt;/code&gt; should confirm your tea order ☕&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This brings us to an important feature in every backend system:&lt;br&gt;
 &lt;strong&gt;Routing&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;what-is-routing-&quot;&gt;What Is Routing?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Routing&lt;/strong&gt; is the logic that tells your server:&lt;br&gt;
➡️ &lt;em&gt;“If a user visits this path (&lt;/em&gt;&lt;code&gt;/&lt;/code&gt;), respond with this content.&lt;br&gt;
If they visit that path (&lt;code&gt;/ice-tea&lt;/code&gt;), respond differently.”&lt;/p&gt;
&lt;p&gt;In simple terms:&lt;br&gt;
&lt;strong&gt;Different URLs → Different responses&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;what-you-ll-learn-in-this-blog&quot;&gt;What You’ll Learn in This Blog&lt;/h3&gt;
&lt;p&gt;We’re going hands-on again — but this time, we’ll build &lt;strong&gt;two smart servers&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;A &lt;strong&gt;Vanilla Node.js&lt;/strong&gt; server (with manual routing using &lt;code&gt;if-else&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A &lt;strong&gt;Bun&lt;/strong&gt; server (with its built-in speedy tools)&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By the end, you'll understand:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;✅ How to respond to specific paths like &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;/login&lt;/code&gt;, &lt;code&gt;/ice-tea&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ How routing works &lt;em&gt;without&lt;/em&gt; any frameworks&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ Why &lt;strong&gt;developer experience matters&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ And why frameworks like &lt;strong&gt;Express.js&lt;/strong&gt; were born to save our sanity&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s start with Node.js.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-building-a-smarter-web-server-in-node-js-vanilla-style-&quot;&gt;️ Building a Smarter Web Server in Node.js (Vanilla Style)&lt;/h2&gt;
&lt;h4 id=&quot;step-1-create-a-file&quot;&gt;Step 1: Create a File&lt;/h4&gt;
&lt;p&gt;Create a new file named:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;server-node.js
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h4 id=&quot;step-2-add-this-code&quot;&gt;Step 2: Add This Code&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const http = require(&quot;http&quot;);

const hostname = &quot;127.0.0.1&quot;;
const port = 3000;

const server = http.createServer((req, res) =&amp;gt; {
  if (req.url === &quot;/&quot;) {
    res.statusCode = 200;
    res.setHeader(&quot;Content-Type&quot;, &quot;text/plain&quot;);
    res.end(&quot;Hello, it's Ice Tea!&quot;);
  } else if (req.url === &quot;/ice-tea&quot;) {
    res.statusCode = 200;
    res.setHeader(&quot;Content-Type&quot;, &quot;text/plain&quot;);
    res.end(&quot;Thanks for ordering ice tea. It's really hot!&quot;);
  } else {
    res.statusCode = 404;
    res.setHeader(&quot;Content-Type&quot;, &quot;text/plain&quot;);
    res.end(&quot;404 - Not Found&quot;);
  }
});

server.listen(port, hostname, () =&amp;gt; {
  console.log(`Server is listening at http://${hostname}:${port}`);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;what-s-happening-here-&quot;&gt;What’s Happening Here?&lt;/h4&gt;
&lt;p&gt;Let’s break it down:&lt;/p&gt;
&lt;h5 id=&quot;req-url&quot;&gt;&lt;code&gt;req.url&lt;/code&gt;&lt;/h5&gt;
&lt;p&gt;This gives us the &lt;strong&gt;path&lt;/strong&gt; the user is trying to access. Examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;/&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;/ice-tea&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;/something-weird&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We use this value to decide &lt;strong&gt;what response&lt;/strong&gt; to send back.&lt;/p&gt;
&lt;h5 id=&quot;if-else-if-else&quot;&gt;&lt;code&gt;if...else if...else&lt;/code&gt;&lt;/h5&gt;
&lt;p&gt;This is basic routing logic:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If the user visits &lt;code&gt;/&lt;/code&gt; → show the homepage&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If they visit &lt;code&gt;/ice-tea&lt;/code&gt; → give them a virtual cup ☕&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If they visit anything else → show a &lt;code&gt;404 - Not Found&lt;/code&gt; message&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;-how-to-run-it&quot;&gt;▶️ How to Run It&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Open your terminal&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run the server:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;node server-node.js
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Open your browser and go to:&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;http://127.0.0.1:3000/&quot;&gt;&lt;code&gt;http://127.0.0.1:3000/&lt;/code&gt;&lt;/a&gt; → You’ll see: &lt;code&gt;Hello, it's Ice Tea!&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;http://127.0.0.1:3000/ice-tea&quot;&gt;&lt;code&gt;http://127.0.0.1:3000/ice-tea&lt;/code&gt;&lt;/a&gt; → You’ll see: &lt;code&gt;Thanks for ordering ice tea. It's really hot!&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;http://127.0.0.1:3000/anything-else&quot;&gt;&lt;code&gt;http://127.0.0.1:3000/anything-else&lt;/code&gt;&lt;/a&gt; → You’ll see: &lt;code&gt;404 - Not Found&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;why-this-matters&quot;&gt;Why This Matters&lt;/h4&gt;
&lt;p&gt;In our first server (from the last post), &lt;strong&gt;we didn’t handle routes at all&lt;/strong&gt;.&lt;br&gt;
The result? Every request got the same reply. That’s not practical.&lt;/p&gt;
&lt;p&gt;This version adds &lt;strong&gt;conditional logic&lt;/strong&gt; — a baby step toward how real websites handle pages, endpoints, and APIs.&lt;/p&gt;
&lt;p&gt;You’ve just built your first &lt;strong&gt;routing mechanism&lt;/strong&gt; in Node.js!&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;rebuilding-the-same-server-with-bun&quot;&gt;Rebuilding the Same Server with Bun&lt;/h2&gt;
&lt;p&gt;We’ve already built a basic web server in Node.js using &lt;code&gt;http.createServer&lt;/code&gt; and some conditional routing.&lt;/p&gt;
&lt;p&gt;Now let’s &lt;strong&gt;recreate the exact same logic&lt;/strong&gt;, but this time using &lt;strong&gt;Bun&lt;/strong&gt; — the newer, faster JavaScript runtime that’s shaking up the backend ecosystem.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;✅ If Node.js feels like a power drill, Bun is more like a Swiss Army knife — fast, modern, and loaded with built-in tools.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h4 id=&quot;step-1-create-a-file-1&quot;&gt;Step 1: Create a File&lt;/h4&gt;
&lt;p&gt;Create a new file named:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;server-bun.js
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h4 id=&quot;step-2-add-this-code-1&quot;&gt;Step 2: Add This Code&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { serve } from &quot;bun&quot;;

serve({
  fetch(req) {
    const url = new URL(req.url);

    if (url.pathname === &quot;/&quot;) {
      return new Response(&quot;Hello, it's Ice Tea!&quot;, { status: 200 });
    } else if (url.pathname === &quot;/ice-tea&quot;) {
      return new Response(&quot;Ice tea is a good option!&quot;, { status: 200 });
    } else {
      return new Response(&quot;404 - Not Found&quot;, { status: 404 });
    }
  },
  port: 3000,
  hostname: &quot;127.0.0.1&quot;
});
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h4 id=&quot;what-s-going-on-here-&quot;&gt;What’s Going On Here?&lt;/h4&gt;
&lt;p&gt;Let’s break it down piece by piece:&lt;/p&gt;
&lt;h5 id=&quot;-import-serve-from-bun-&quot;&gt;✅ &lt;code&gt;import { serve } from &quot;bun&quot;&lt;/code&gt;&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;No need for &lt;code&gt;http&lt;/code&gt; module or third-party packages — Bun comes with &lt;code&gt;serve&lt;/code&gt; &lt;strong&gt;built-in&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This function spins up a server instantly with minimal setup&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;-fetch-req-&quot;&gt;✅ &lt;code&gt;fetch(req)&lt;/code&gt;&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Instead of &lt;code&gt;createServer((req, res) =&amp;gt; {})&lt;/code&gt;, Bun uses a &lt;code&gt;fetch()&lt;/code&gt;-like handler&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you've ever used &lt;code&gt;fetch()&lt;/code&gt; in the browser, this will feel familiar&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This function receives the request and is expected to return a &lt;code&gt;Response&lt;/code&gt; object&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;-new-url-req-url-&quot;&gt;✅ &lt;code&gt;new URL(req.url)&lt;/code&gt;&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;This gives you an easy way to extract the &lt;code&gt;pathname&lt;/code&gt; (like &lt;code&gt;/&lt;/code&gt; or &lt;code&gt;/ice-tea&lt;/code&gt;) from the request URL&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Think of it like a more modern and cleaner way than parsing &lt;code&gt;req.url&lt;/code&gt; manually in Node&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;-return-new-response-&quot;&gt;✅ &lt;code&gt;return new Response(...)&lt;/code&gt;&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;Just like the browser's &lt;code&gt;fetch()&lt;/code&gt; API — you return a &lt;code&gt;Response&lt;/code&gt; object with a body and status code&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;what-you-just-did&quot;&gt;What You Just Did&lt;/h4&gt;
&lt;p&gt;You created a complete HTTP server using only &lt;strong&gt;10 lines of code&lt;/strong&gt;, with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;No external dependencies&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No manual headers&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No boilerplate setup&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And it &lt;strong&gt;still behaves exactly like the Node.js version&lt;/strong&gt;:&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;-how-to-run-it-1&quot;&gt;▶️ How to Run It&lt;/h4&gt;
&lt;p&gt;Make sure you have Bun installed. Then run:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;bun server-bun.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, visit these URLs in your browser:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;URL &lt;/th&gt;&lt;th&gt;Response&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;http://127.0.0.1:3000/&quot;&gt;&lt;code&gt;http://127.0.0.1:3000/&lt;/code&gt;&lt;/a&gt; &lt;/td&gt;&lt;td&gt;&lt;code&gt;Hello, it's Ice Tea!&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;http://127.0.0.1:3000/ice-tea&quot;&gt;&lt;code&gt;http://127.0.0.1:3000/ice-tea&lt;/code&gt;&lt;/a&gt; &lt;/td&gt;&lt;td&gt;&lt;code&gt;Ice tea is a good option!&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;http://127.0.0.1:3000/random&quot;&gt;&lt;code&gt;http://127.0.0.1:3000/random&lt;/code&gt;&lt;/a&gt; &lt;/td&gt;&lt;td&gt;&lt;code&gt;404 - Not Found&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&quot;key-learnings-node-js-vs-bun-syntax-comparison&quot;&gt;Key Learnings – Node.js vs Bun Syntax Comparison&lt;/h2&gt;
&lt;p&gt;Let’s break down how Node.js and Bun differ in terms of syntax and developer experience:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;strong&gt;Feature&lt;/strong&gt; &lt;/th&gt;&lt;th&gt;&lt;strong&gt;Node.js&lt;/strong&gt; &lt;/th&gt;&lt;th&gt;&lt;strong&gt;Bun&lt;/strong&gt;&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Imports&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;&lt;code&gt;require(&quot;http&quot;)&lt;/code&gt; &lt;/td&gt;&lt;td&gt;&lt;code&gt;import { serve } from &quot;bun&quot;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Server creation&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;&lt;code&gt;http.createServer()&lt;/code&gt; &lt;/td&gt;&lt;td&gt;&lt;code&gt;serve()&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Request object&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;&lt;code&gt;req.url&lt;/code&gt; &lt;/td&gt;&lt;td&gt;&lt;code&gt;new URL(req.url)&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Response&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;&lt;code&gt;res.writeHead(), res.end()&lt;/code&gt; &lt;/td&gt;&lt;td&gt;&lt;code&gt;return new Response(...)&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Port/Host setup&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;&lt;code&gt;server.listen(port, host)&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Included directly in &lt;code&gt;serve({})&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;File support&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;CommonJS (&lt;code&gt;.js&lt;/code&gt;) &lt;/td&gt;&lt;td&gt;ESM-first (&lt;code&gt;.js&lt;/code&gt;, &lt;code&gt;.ts&lt;/code&gt;, &lt;code&gt;.jsx&lt;/code&gt;, etc.)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Auto Restart&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;❌ Manual restart required &lt;/td&gt;&lt;td&gt;❌ Not built-in (use &lt;code&gt;bunx nodemon&lt;/code&gt; or &lt;code&gt;bun --watch&lt;/code&gt;)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-the-problem-code-reusability-amp-scalability&quot;&gt;⚠️ The Problem: Code Reusability &amp;amp; Scalability&lt;/h2&gt;
&lt;p&gt;Let’s face it — even with just &lt;strong&gt;3 routes&lt;/strong&gt;, our &lt;code&gt;if-else&lt;/code&gt; chain already looks messy.&lt;/p&gt;
&lt;p&gt;Imagine managing 20+ routes in a real-world API this way. &lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Problem &lt;/th&gt;&lt;th&gt;Why It Matters&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;❌ Hard to read &lt;/td&gt;&lt;td&gt;Too much nesting, unclear logic&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;❌ Not modular &lt;/td&gt;&lt;td&gt;All logic in one file — no separation&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;❌ Difficult to test &lt;/td&gt;&lt;td&gt;No reusable structure, can't isolate parts&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;the-solution-use-a-framework&quot;&gt;The Solution: Use a Framework&lt;/h2&gt;
&lt;p&gt;That’s why developers turn to &lt;strong&gt;backend frameworks&lt;/strong&gt; — to make their code cleaner, modular, and scalable.&lt;/p&gt;
&lt;p&gt;Some popular options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Express&lt;/strong&gt; (Node.js) – industry standard&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hono&lt;/strong&gt; (Bun) – minimal, super fast&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fastify&lt;/strong&gt; – fast, schema-based&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Koa&lt;/strong&gt; – modern, middleware-centric&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These frameworks provide:&lt;/p&gt;
&lt;p&gt;✅ Route grouping&lt;br&gt;
✅ Middleware support (auth, logging, etc.)&lt;br&gt;
✅ Centralized error handling&lt;br&gt;
✅ Clean, readable syntax&lt;br&gt;
✅ Better DX (Developer Experience)&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-s-next-&quot;&gt;What’s &lt;code&gt;Next&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;In the next blog, we’ll take things to the next level:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Explore backend frameworks&lt;/strong&gt; (not just Express)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Start using Express.js&lt;/strong&gt; to build cleaner, modular, and testable APIs&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Learn about middleware, route grouping, and error handling&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;You’ve just taken your first real step toward backend mastery.&lt;/strong&gt;&lt;br&gt;
If you found this post helpful, just wait till we dive into frameworks — things are about to get &lt;em&gt;way more powerful&lt;/em&gt; (and easier)!&lt;/p&gt;
&lt;p&gt;Stay tuned.&lt;/p&gt;
</description>
<category>Setup Guide</category>
<guid isPermaLink="true">https://coderlegion.com/4293/building-web-servers-with-node-js-and-bun-a-backend-developers-first-step</guid>
<pubDate>Mon, 28 Jul 2025 06:12:04 +0000</pubDate>
</item>
</channel>
</rss>