<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged expressjs</title>
<link>https://coderlegion.com/tag/expressjs</link>
<description>Powered by Question2Answer</description>
<item>
<title>How i build rusu-ionut.ro v2</title>
<link>https://coderlegion.com/31743/how-i-build-rusu-ionut-ro-v2</link>
<description>&lt;p&gt;Most personal sites are a handful of pages that you edit in code, rebuild and upload. Mine has no pages in the code at all. rusu-ionut.ro is a small content management system that I built for myself: the pages, menus, blog articles and portfolio projects live in a database, and the site draws whatever the API returns for the address you opened.&lt;/p&gt;
&lt;p&gt;This article is the tour I would have wanted before starting: how the site is put together, what is in it, how I keep it from breaking, and the handful of lessons that cost me the most time. The first commit of this rebuild is dated 25 September 2026, and at the time of writing the site is already on version 1.29.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;img src=&quot;https://rusu-ionut.ro/uploads/2026/10/rusu-ionut-ro-admin-5dcb5c0d.png&quot; alt=&quot;image&quot;&gt;&lt;/strong&gt; Home page, dark theme, desktop width. Use this one as the article cover.&lt;/p&gt;
&lt;h2&gt;The idea: a site with no hardcoded pages&lt;/h2&gt;
&lt;p&gt;The rule I started from is simple: a new public URL should be a piece of content, not a code change. If I want a new page I open the backoffice, give it a title, a place in the page tree and a layout, and drop content blocks onto it. No build, no deploy, no pull request.&lt;/p&gt;
&lt;p&gt;That rule shaped everything else. Visitors never sign in, because the public side has no user accounts. Only the owner and editors sign in, and only to a separate backoffice. The public site is a read-only reflection of the content.&lt;/p&gt;
&lt;p&gt;It is more moving parts than a static site generator, and I will not pretend otherwise. What I get in return is editing from any browser, a real media library, drafts, ordering by drag and drop, and a clean split between content (data) and presentation (components).&lt;/p&gt;
&lt;h2&gt;The stack at a glance&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Front end:&lt;/strong&gt; Vue 3 with TypeScript and Vite, Vuetify 4 for components, Tailwind CSS 4 for layout and utility styling, Pinia for state, Vue Router, Axios for HTTP, TipTap for rich-text editing and ApexCharts for the dashboard.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Back end:&lt;/strong&gt; Node.js with Express 5 and TypeScript, Mongoose for the models, Zod for validating every write, Helmet for secure headers and Multer for uploads.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt; MongoDB.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tooling:&lt;/strong&gt; ESLint, Prettier, Husky, Vitest and Playwright.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Two apps, one repository. The layout is deliberately boring:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;client/    Vue single-page app (public site and backoffice)
server/    Express API: routes, controllers, services, models
scripts/   Repository tooling
e2e/       Playwright specs that drive the real site&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On the server the flow is always the same: a router handles the path, a Zod schema validates the body, a thin controller delegates, and a service holds the business logic. On the client, a view only composes components, composables fetch and shape data, and constants hold every label. I come back to why that matters in the section on rules.&lt;/p&gt;
&lt;h2&gt;How a page is built&lt;/h2&gt;
&lt;p&gt;A page is a node in a tree with a path, a layout and a list of blocks. Each block sits in one of four regions: banner, main, sidebar or footer. When you open any address that is not an article or a project, a single catch-all view asks the API for the page at that path and hands its blocks to a renderer. The home page is simply the page with an empty path.&lt;/p&gt;
&lt;p&gt;The renderer is a lookup table from block type to Vue component. There are eleven types: free rich text, plus ten widgets (fact cards, skills, timeline, logo grid, certifications, profile card, contact form, blog feed, portfolio list and promo banner).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// simplified: one entry per block type
const componentMap = {
  html: HtmlBlock,
  widget_skills: WidgetSkills,
  widget_timeline: WidgetTimeline,
  widget_blog_feed: WidgetBlogFeed,
  // ...
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adding a new kind of block means touching exactly two places: the editor's registry, which tells the backoffice which fields to offer, and this renderer. Keeping the two in sync is a rule, so the editor never offers a block the site cannot draw.&lt;/p&gt;
&lt;p&gt;Blocks below the fold are not rendered until they are about to scroll into view, and heavier widgets load on demand behind a small loading bar, so the first screen stays light.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;img src=&quot;https://rusu-ionut.ro/uploads/2026/10/rusu-ionut-ro-admin-5dcb5c0d.png&quot; alt=&quot;image&quot;&gt;&lt;/strong&gt; Backoffice page editor with the four regions (banner, main, sidebar, footer) and a few blocks placed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;img src=&quot;https://rusu-ionut.ro/uploads/2026/10/rusu-ionut-ro-admin-blocks-af7f817d.png&quot; alt=&quot;image&quot;&gt;&lt;/strong&gt; The add-block picker showing the block types.&lt;/p&gt;
&lt;h2&gt;The backoffice&lt;/h2&gt;
&lt;p&gt;The backoffice is a separate part of the same app, with its own shell and its own light and dark switch, independent of the public theme. A short tour:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dashboard:&lt;/strong&gt; a chart of how much content there is, a gauge of the average site rating, the latest inquiries, recent sign-ins and a visitors overview.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Search:&lt;/strong&gt; one box that searches blocks, pages, articles, projects and menu links. It shows the text that matched and, for a block, which pages use it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pages, blocks and menus:&lt;/strong&gt; a page tree, a reusable block library, and header, footer and social menus that reorder by drag and drop.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Media library:&lt;/strong&gt; uploads with type and size limits, previews, file details and a picker that every editor reuses.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Articles and projects:&lt;/strong&gt; full editors with cover images, categories, tags, publish dates and a draft switch.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Audience:&lt;/strong&gt; an inbox for contact messages, newsletter subscribers, ratings and a log of sign-ins.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Settings:&lt;/strong&gt; owner-only switches, such as taking the site offline for maintenance or showing the ratings widget.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are two roles. Editors create and update content; owners can also delete things and change settings.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;img src=&quot;https://rusu-ionut.ro/uploads/2026/10/rusu-ionut-ro-admin-5dcb5c0d.png&quot; alt=&quot;image&quot;&gt;&lt;/strong&gt; The dashboard with the visitors section visible. Blur or crop the inquiries and sign-in lists first, since they show real people.&lt;/p&gt;
&lt;h3&gt;The rich-text editor, and not rewriting your content&lt;/h3&gt;
&lt;p&gt;Articles and text blocks are written in an editor built on TipTap, with three views: Visual, HTML and Preview. The preview shows the content with the site's own styling, in light or dark.&lt;/p&gt;
&lt;p&gt;The detail I am most pleased with is small. A visual editor only understands the markup it was built for; hand it anything else and it quietly drops it on the next edit. So before opening content in Visual mode, the editor checks whether it contains anything it would lose: unsupported tags, attributes or inline styles. If it does, the content opens in HTML mode with a warning, instead of being rewritten behind my back.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;img src=&quot;https://rusu-ionut.ro/uploads/2026/10/rusu-ionut-ro-admin-articles-new-33ad35ca.png&quot; alt=&quot;image&quot;&gt;&lt;/strong&gt; The article editor showing the Visual, HTML and Preview switch and the toolbar.&lt;/p&gt;
&lt;h2&gt;The blog&lt;/h2&gt;
&lt;p&gt;The blog you are reading is part of the same system. Articles have a cover image, a category, tags, a publish date and a summary. When I leave the summary empty, the site cuts an excerpt from the first lines of the article for the cards.&lt;/p&gt;
&lt;p&gt;The list supports search, category and tag filters, and pagination, and every article shows its reading time. On an article page you can change the text size (it only affects the open article and resets when you leave), copy the link, or open a QR code for the page. The article ends with an author card with ways to get in touch.&lt;/p&gt;
&lt;p&gt;Article HTML is cleaned before it is drawn, so even if something odd ends up in a body it cannot run a script on a visitor's page.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;img src=&quot;https://rusu-ionut.ro/uploads/2026/10/rusu-ionut-ro-admin-articles-new-33ad35ca.png&quot; alt=&quot;image&quot;&gt;&lt;/strong&gt; An article page on desktop, showing the text-size control and the share panel.&lt;/p&gt;
&lt;h2&gt;The portfolio&lt;/h2&gt;
&lt;p&gt;Projects have their own cards, which you can view as a grid or a list, and their own pages with the story, the technologies and links to the live site and the source. You can search by name or technology and narrow the list with technology chips. The count updates as you filter, and an empty result offers a one-click way back to everything.&lt;/p&gt;
&lt;p&gt;A promo banner block can also spotlight one project on any page: a headline, a short pitch, up to two buttons and a screenshot with a gentle float or tilt. A thin light travels around its border. All of that motion switches off for visitors who prefer reduced motion.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;img src=&quot;https://rusu-ionut.ro/uploads/2026/10/rusu-ionut-ro-admin-portfolio-353cc28c.png&quot; alt=&quot;image&quot;&gt;&lt;/strong&gt; The portfolio list with the search field and technology chips, or a single project page.&lt;/p&gt;
&lt;h2&gt;A site that feels alive&lt;/h2&gt;
&lt;p&gt;Visitors get dark by default, with a light switch. Both themes come from one set of colour tokens shared by Tailwind and Vuetify, so a button, a card and a chart never disagree about what &quot;primary&quot; means.&lt;/p&gt;
&lt;p&gt;On the first visit a startup splash holds until the page's content has actually arrived, then fades. You see one welcome instead of a welcome followed by a second wait. The home page hero is an illustrated landscape: drifting clouds and a sun by day; stars, shooting stars and fireflies at night; and a cyclist riding along the crest of the hills. Inner pages have a calmer mountain version. Everything animated respects reduced motion.&lt;/p&gt;
&lt;p&gt;The unglamorous states are designed too: a friendly 404, a retry page for when something fails to load, a maintenance page, an unexpected-error screen, and a contact form that guides you through a message and confirms that it was sent. A small feedback invitation and a rating dialog collect opinions, and on phones the invitation starts minimised so it never covers the content.&lt;/p&gt;
&lt;p&gt;There is also a standalone résumé page that prints cleanly, and an in-app changelog: click the version number in the footer to see what changed and when.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;img src=&quot;https://rusu-ionut.ro/uploads/2026/10/screenshot-2026-10-19-at-17-32-32-ionu-rusu-fullstack-develo-f35d89ea.png&quot; alt=&quot;image&quot;&gt;&lt;/strong&gt; The same home hero in dark and light theme, side by side.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;img src=&quot;https://rusu-ionut.ro/uploads/2026/10/screenshot-2026-10-19-173425-eddc95f7.png&quot; alt=&quot;image&quot;&gt;&lt;/strong&gt; One page at phone, tablet and desktop width, side by side.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;img src=&quot;https://rusu-ionut.ro/uploads/2026/10/tablet-9d14cda6.png&quot; alt=&quot;image&quot;&gt;&lt;/strong&gt; One or two of the friendly states, such as the 404 or the maintenance page.&lt;/p&gt;
&lt;h2&gt;Findable and shareable&lt;/h2&gt;
&lt;p&gt;Each page and article has its own title and description, a canonical address, Open Graph and Twitter metadata for link previews, and structured data. The sitemap is generated from the published content, so it never goes stale. On the page itself, sharing is one click away, with a copy-link control and a QR code for moving from a laptop to a phone.&lt;/p&gt;
&lt;h2&gt;Visitor statistics without the creepy parts&lt;/h2&gt;
&lt;p&gt;I wanted to know which pages and countries bring people in, without handing visitors to a third-party tracker. The result is a small, built-in counter with a few firm rules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No analytics cookies, and nothing is written to the visitor's browser.&lt;/li&gt;
&lt;li&gt;Only the page path is recorded, with query strings and fragments thrown away. Admin screens, previews and obvious bots are excluded, and Do Not Track and Global Privacy Control are honoured.&lt;/li&gt;
&lt;li&gt;Unique visitors are estimated without storing an IP address or a browser signature, and the records expire automatically after a few months.&lt;/li&gt;
&lt;li&gt;Countries are worked out locally from a bundled public-domain dataset, so no visitor information is sent to a lookup service.&lt;/li&gt;
&lt;li&gt;If collecting a visit fails, nothing the visitor sees is affected.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The trade-off is honesty about accuracy. It is an estimate, shared connections and VPNs blur it, and it only counts from the day it was switched on.&lt;/p&gt;
&lt;h2&gt;Security, in plain words&lt;/h2&gt;
&lt;p&gt;I will keep this section deliberately general, because how protection works is not something to publish. The principles are ordinary ones, applied everywhere. Every write is validated against a schema before it touches the database. Anything that renders rich content is sanitised first. Roles limit who can delete or change settings, and the API is throttled. Secrets stay in configuration and never in code, and signing in to the backoffice takes more than a password. Errors leave the API as short codes rather than stack traces, and the backoffice turns them into plain sentences.&lt;/p&gt;
&lt;h2&gt;Rules, tests and a changelog&lt;/h2&gt;
&lt;p&gt;A project that grows by a feature a day will rot quickly unless something pushes back. In this repository the pushback is a rulebook, AGENTS.md, that binds every contributor, human or AI assistant. It is long, but the ideas are few:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;One source of truth.&lt;/strong&gt; Colours come from theme tokens, user-facing text from one dictionary, and option values and icon names from a registry of literals. Writing &quot;flat&quot; or &quot;mdi-plus&quot; inline in a template is a violation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Screens are orchestrators.&lt;/strong&gt; A new screen only composes smaller components and composables. It never owns the markup of every part.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A fixed file order.&lt;/strong&gt; Template first, then the script with its sections in a set order, then styles only when Tailwind and Vuetify cannot do the job.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Leave it cleaner.&lt;/strong&gt; Every change ends with a cleanup pass: dead styles, unused imports, orphaned logic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Preserve the design.&lt;/strong&gt; A layout that is already on screen is the design. Fixing one thing must not move another.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is what the literals rule looks like in practice:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- before --&amp;gt;

  Save


&amp;lt;!-- after: same pixels, every value sourced --&amp;gt;

  {{ LABELS.SAVE }}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It looks like ceremony until the day a label changes or a button style needs to move, and it is a one-line edit instead of a search through every file.&lt;/p&gt;
&lt;h3&gt;Tests that look at the real site&lt;/h3&gt;
&lt;p&gt;Unit tests (Vitest, twenty files today) cover pure logic: text helpers, validation schemas, the visitor-counting rules, the splash screen's timing. The part I care about more is the browser suite: 19 Playwright specs that drive the running site at phone, tablet and desktop widths. The rules for those specs are all there because something went wrong without them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Every user-visible feature ships with a spec that covers all of its states: loading, loaded, failed and empty. The states are produced by holding, failing or faking the network request, not by hoping the database happens to hold the right data.&lt;/li&gt;
&lt;li&gt;Assert on what is painted, not on its wrapper. A box can have the right size while the image inside it is blank.&lt;/li&gt;
&lt;li&gt;Wait out the startup splash, because it covers the page while the tests still count the content as visible.&lt;/li&gt;
&lt;li&gt;Run each spec at least twice. A flaky test is fixed at its cause, never with a retry or a longer timeout.&lt;/li&gt;
&lt;li&gt;Look at the screenshots. A passing assertion behind an overlay proves nothing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A Husky hook runs lint, formatting, the unit tests and both builds before a commit is accepted.&lt;/p&gt;
&lt;p&gt;A Playwright run in the terminal, or its screenshots at the three widths.&lt;/p&gt;
&lt;h3&gt;A changelog people can read&lt;/h3&gt;
&lt;p&gt;Every finished feature or fix bumps the version in lockstep across the packages and adds an entry to the in-app changelog. That changelog has its own rule: milestones only, written for visitors, in plain language, never describing how something works or what it runs on. It is the one place where the project talks to its readers, so it avoids the internals on purpose. Version 1.29 is summed up as understand your audience, a project spotlight, a page for every project and a rider on the hills.&lt;/p&gt;
&lt;p&gt;One more honest note. The rulebook is written for contributors and AI assistants alike, and I do use assistants on this project. The rulebook is what keeps that work consistent, reviewable and verified, whoever or whatever writes the code.&lt;/p&gt;
&lt;h2&gt;What I learned along the way&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Same names, different layers.&lt;/strong&gt; Vuetify ships helper classes named like Tailwind's (&lt;code&gt;pt-16&lt;/code&gt;, &lt;code&gt;justify-start&lt;/code&gt;), and they sit in a CSS layer that outranks Tailwind's responsive variants. A &lt;code&gt;sm:&lt;/code&gt; override that looks right in the code silently loses. The fix is to write the value that must win as an arbitrary value, such as &lt;code&gt;pt-[4rem]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hidden is not clipped.&lt;/strong&gt; A decorative light travelling around a banner's border was wider than a phone's side margin. Masking it hid the light but did not stop it from widening the page, so the home page slid sideways on phones. Each ring now clips its own light.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A test can pass behind a curtain.&lt;/strong&gt; Playwright counts the content under the startup splash as visible, so an assertion can pass while the screenshot shows only the splash. Waiting out the splash is now part of every spec.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Start-up cost is user-facing.&lt;/strong&gt; The first visit after a quiet spell is the one that makes the impression. I trimmed what the server does at boot, so heavy libraries now load on first use, and the splash waits for real content to avoid a second wait.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Constraints are a feature.&lt;/strong&gt; A rule like &quot;no new local constants for values that already have a home&quot; feels slow for a day, and then it pays back every time a label or a colour has to change.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Where to look next&lt;/h2&gt;
&lt;p&gt;If you want the short version: a Vue 3 front end and an Express API around a MongoDB database, where pages are data, blocks are components, the backoffice is the only place anything is written, and a rulebook plus a real-browser test suite keeps the whole thing honest.&lt;/p&gt;
&lt;p&gt;Click the version number in the footer to read the release history, browse the projects to see what else I have built, and if you have a question about any of this, the contact page is a message away.&lt;/p&gt;
</description>
<category>Launches</category>
<guid isPermaLink="true">https://coderlegion.com/31743/how-i-build-rusu-ionut-ro-v2</guid>
<pubDate>Mon, 19 Oct 2026 14:40:54 +0000</pubDate>
</item>
<item>
<title>From CRUD APIs to Production-Style Backend Architecture: My Video Sharing Platform Project</title>
<link>https://coderlegion.com/19204/from-crud-apis-to-production-style-backend-architecture-video-sharing-platform-project</link>
<description>&lt;p&gt; Completed a Production-Style Video Sharing Platform Backend API&lt;br&gt;
Over the past few weeks, I've been building a backend system inspired by modern video-sharing platforms using Node.js, Express.js, MongoDB, and Cloudinary.&lt;/p&gt;
&lt;p&gt;This project went far beyond CRUD operations and helped me understand how real backend systems are designed.&lt;/p&gt;
&lt;p&gt;Features Implemented:&lt;br&gt;
✅ JWT Authentication &amp;amp; Refresh Token Rotation&lt;br&gt;
✅ Secure HTTP-Only Cookie Based Sessions&lt;br&gt;
✅ Video Upload &amp;amp; Cloudinary Integration&lt;br&gt;
✅ Comment System&lt;br&gt;
✅ Like System (Videos, Comments, Tweets)&lt;br&gt;
✅ Subscription System&lt;br&gt;
✅ Playlist Management&lt;br&gt;
✅ Watch History Tracking&lt;br&gt;
✅ Creator Dashboard Analytics&lt;br&gt;
✅ Pagination &amp;amp; Aggregation Pipelines&lt;/p&gt;
&lt;p&gt;Key Things I Learned:&lt;br&gt;
 Authentication using Access &amp;amp; Refresh Tokens&lt;br&gt;
 Designing relationships in MongoDB&lt;br&gt;
 MongoDB Aggregation Pipelines ($lookup, $group, $project, $addFields)&lt;br&gt;
 Ownership-Based Authorization&lt;br&gt;
 Cloudinary Media Management&lt;br&gt;
 Cascade Cleanup Logic&lt;br&gt;
 Building consistent API response structures&lt;/p&gt;
&lt;p&gt;Interesting Debugging Challenges:&lt;br&gt;
Duplicate MongoDB index issues&lt;br&gt;
Populate path errors&lt;br&gt;
Refresh token validation flow&lt;br&gt;
Route mismatches&lt;br&gt;
Aggregation pipeline bugs&lt;br&gt;
Watch history synchronization&lt;br&gt;
Video deletion cleanup across collections&lt;/p&gt;
&lt;p&gt;Documentation Created:&lt;br&gt;
 Complete API Documentation&lt;br&gt;
 Authentication Flow Documentation&lt;br&gt;
️ Database Schema Documentation&lt;/p&gt;
&lt;p&gt;What's Next?&lt;br&gt;
Building the React frontend and converting this into a complete full-stack application.&lt;/p&gt;
&lt;p&gt;GitHub Repository: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/SairajJadhav08/Video-Sharing-Platform-API&quot;&gt;https://github.com/SairajJadhav08/Video-Sharing-Platform-API&lt;/a&gt;&lt;/p&gt;
</description>
<category>Discussions</category>
<guid isPermaLink="true">https://coderlegion.com/19204/from-crud-apis-to-production-style-backend-architecture-video-sharing-platform-project</guid>
<pubDate>Fri, 29 May 2026 09:30:58 +0000</pubDate>
</item>
<item>
<title>Node.js &amp; Express.js: The Ultimate, Comprehensive, and Extremely Detailed Guide for Developers</title>
<link>https://coderlegion.com/1299/node-js-express-the-ultimate-comprehensive-and-extremely-detailed-guide-for-developers</link>
<description>&lt;h2 id=&quot;node-js-amp-express-js-the-ultimate-comprehensive-and-extremely-detailed-guide-for-developers&quot;&gt;&lt;strong&gt;Node.js &amp;amp; Express.js: The Ultimate, Comprehensive, and Extremely Detailed Guide for Developers&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Welcome to the &lt;strong&gt;most extensive&lt;/strong&gt;, &lt;strong&gt;most detailed&lt;/strong&gt;, and &lt;strong&gt;most helpful&lt;/strong&gt; guide on &lt;strong&gt;Node.js&lt;/strong&gt; and &lt;strong&gt;Express.js&lt;/strong&gt; ever written! This post is designed to be &lt;strong&gt;so long&lt;/strong&gt;, &lt;strong&gt;so comprehensive&lt;/strong&gt;, and &lt;strong&gt;so packed with advanced knowledge&lt;/strong&gt; that you’ll feel like a Node.js and Express.js wizard by the end of it. ✨ Whether you’re a beginner or an expert, this guide will take your skills to the next level. Let’s dive in! &lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-is-node-js-why-is-it-so-important-&quot;&gt;&lt;strong&gt;What is Node.js? Why is it So Important?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Node.js is not just another JavaScript runtime—it’s a &lt;strong&gt;revolutionary technology&lt;/strong&gt; that has transformed backend development forever. Built on Chrome's V8 JavaScript engine, Node.js allows developers to run JavaScript on the server side. This means you can use the same language (JavaScript) for both frontend and backend development—how amazing is that? &lt;/p&gt;
&lt;h4 id=&quot;why-choose-node-js-&quot;&gt;&lt;strong&gt;Why Choose Node.js?&lt;/strong&gt;&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Non-blocking I/O Model&lt;/strong&gt;: Node.js uses an event-driven, non-blocking I/O model, making it lightweight and efficient. This is perfect for building real-time applications like chat apps, gaming servers, and streaming platforms.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vast Ecosystem&lt;/strong&gt;: With over &lt;strong&gt;1 million packages&lt;/strong&gt; available on npm (Node Package Manager), Node.js has one of the largest ecosystems of libraries and tools.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scalability&lt;/strong&gt;: Node.js is designed to handle thousands of concurrent connections, making it ideal for high-traffic applications.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-platform&lt;/strong&gt;: Node.js works seamlessly across different operating systems, ensuring your code runs everywhere.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Community Support&lt;/strong&gt;: Node.js has a massive, active community of developers who contribute to its growth and provide support through forums, tutorials, and open-source projects.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-is-express-js-why-is-it-so-essential-&quot;&gt;&lt;strong&gt;What is Express.js? Why is it So Essential?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Express.js is a &lt;strong&gt;minimalist and flexible&lt;/strong&gt; web application framework for Node.js. It simplifies the process of building robust APIs and web applications by providing a set of powerful features like routing, middleware support, and templating. Express.js is like the &lt;strong&gt;Swiss Army Knife&lt;/strong&gt; of web development—it’s versatile, reliable, and indispensable.&lt;/p&gt;
&lt;h4 id=&quot;why-use-express-js-&quot;&gt;&lt;strong&gt;Why Use Express.js?&lt;/strong&gt;&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Simplicity&lt;/strong&gt;: Express.js is easy to learn and use, even for beginners. Its minimalistic design allows you to build applications quickly without unnecessary complexity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Middleware Support&lt;/strong&gt;: Middleware functions are the backbone of Express.js. They allow you to handle requests, responses, and errors in a modular way.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Routing&lt;/strong&gt;: Express.js provides a powerful routing mechanism, enabling you to define endpoints and handle HTTP methods (GET, POST, PUT, DELETE) effortlessly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extensibility&lt;/strong&gt;: Express.js is highly extensible, allowing you to integrate third-party libraries and plugins for additional functionality.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt;: Express.js is lightweight and fast, making it an excellent choice for building high-performance applications.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-setting-up-your-environment-the-most-detailed-guide-ever&quot;&gt;️ &lt;strong&gt;Setting Up Your Environment: The Most Detailed Guide Ever&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Before diving into coding, let’s set up your development environment. We’ll go step-by-step to ensure everything is crystal clear.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Install Node.js&lt;/strong&gt;: Download and install Node.js from &lt;a rel=&quot;nofollow&quot; href=&quot;https://nodejs.org/&quot;&gt;nodejs.org&lt;/a&gt;. This will also install &lt;code&gt;npm&lt;/code&gt; (Node Package Manager).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Initialize a Project&lt;/strong&gt;: Create a new directory and run &lt;code&gt;npm init&lt;/code&gt; to generate a &lt;code&gt;package.json&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Install Express.js&lt;/strong&gt;: Run &lt;code&gt;npm install express&lt;/code&gt; to add Express.js to your project.&lt;/p&gt;
&lt;p&gt; &lt;code&gt;`bash
 mkdir my-app
 cd my-app
 npm init -y
 npm install express
 &lt;/code&gt;`&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;building-your-first-express-js-application-the-most-beginner-friendly-example&quot;&gt;&lt;strong&gt;Building Your First Express.js Application: The Most Beginner-Friendly Example&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Let’s create a simple &quot;Hello World&quot; application using Express.js:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;```javascript
const express = require('express');
const app = express();
const port = 3000;

// Define a route
app.get('/', (req, res) =&amp;gt; {
  res.send('Hello, World! ');
});

// Start the server
app.listen(port, () =&amp;gt; {
  console.log(`Server is running at http://localhost:${port}`);
});
```
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the app using &lt;code&gt;node app.js&lt;/code&gt;, and visit &lt;code&gt;http://localhost:3000&lt;/code&gt; in your browser. You should see &quot;Hello, World! &quot;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;advanced-concepts-in-node-js-amp-express-js-the-most-in-depth-guide-ever&quot;&gt;&lt;strong&gt;Advanced Concepts in Node.js &amp;amp; Express.js: The Most In-Depth Guide Ever&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Now that you’ve built a basic app, let’s explore some advanced topics to take your skills to the next level! &lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;1-middleware-in-express-js-the-backbone-of-every-application&quot;&gt;1️⃣ &lt;strong&gt;Middleware in Express.js: The Backbone of Every Application&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Middleware functions are the backbone of Express.js. They have access to the request (&lt;code&gt;req&lt;/code&gt;), response (&lt;code&gt;res&lt;/code&gt;), and the next middleware function in the stack. Here’s how you can use middleware:&lt;/p&gt;
&lt;h5 id=&quot;example-logging-middleware&quot;&gt;Example: Logging Middleware&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;```javascript
app.use((req, res, next) =&amp;gt; {
  console.log(`${req.method} ${req.url}`);
  next(); // Pass control to the next middleware
});
```
&lt;/code&gt;&lt;/pre&gt;
&lt;h5 id=&quot;popular-middleware-libraries-&quot;&gt;Popular Middleware Libraries:&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;morgan&lt;/strong&gt;: HTTP request logger.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;body-parser&lt;/strong&gt;: Parses incoming request bodies.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;cors&lt;/strong&gt;: Enables Cross-Origin Resource Sharing.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;2-routing-in-express-js-the-most-powerful-feature&quot;&gt;2️⃣ &lt;strong&gt;Routing in Express.js: The Most Powerful Feature&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Routing allows you to define multiple endpoints for your application. Here’s an example of a RESTful API:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;```javascript
// GET all users
app.get('/users', (req, res) =&amp;gt; {
  res.json([{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }]);
});

// GET a single user
app.get('/users/:id', (req, res) =&amp;gt; {
  const userId = req.params.id;
  res.json({ id: userId, name: 'John' });
});

// POST a new user
app.post('/users', (req, res) =&amp;gt; {
  const newUser = req.body;
  res.status(201).json(newUser);
});
```
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h4 id=&quot;3-error-handling-the-most-critical-aspect&quot;&gt;3️⃣ &lt;strong&gt;Error Handling: The Most Critical Aspect&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Proper error handling is crucial for building robust applications. Express.js allows you to define custom error-handling middleware:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;```javascript
app.use((err, req, res, next) =&amp;gt; {
  console.error(err.stack);
  res.status(500).send('Something broke! ');
});
```
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h4 id=&quot;4-templating-engines-the-most-flexible-way-to-render-dynamic-content&quot;&gt;4️⃣ &lt;strong&gt;Templating Engines: The Most Flexible Way to Render Dynamic Content&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Express.js supports various templating engines like &lt;strong&gt;EJS&lt;/strong&gt;, &lt;strong&gt;Pug&lt;/strong&gt;, and &lt;strong&gt;Handlebars&lt;/strong&gt;. These engines allow you to render dynamic HTML pages.&lt;/p&gt;
&lt;h5 id=&quot;example-with-ejs-&quot;&gt;Example with EJS:&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;Install EJS: &lt;code&gt;npm install ejs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Set the view engine:&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;app.set('view engine', 'ejs');
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Render a template:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;```javascript
   app.get('/', (req, res) =&amp;gt; {
     res.render('index', { title: 'My App', message: 'Welcome!' });
   });
   ```
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h4 id=&quot;5-authentication-amp-authorization-the-most-secure-practices&quot;&gt;5️⃣ &lt;strong&gt;Authentication &amp;amp; Authorization: The Most Secure Practices&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Securing your application is essential. Use libraries like &lt;strong&gt;Passport.js&lt;/strong&gt; or &lt;strong&gt;JWT (JSON Web Tokens)&lt;/strong&gt; for authentication.&lt;/p&gt;
&lt;h5 id=&quot;example-with-jwt-&quot;&gt;Example with JWT:&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;```javascript
const jwt = require('jsonwebtoken');

app.post('/login', (req, res) =&amp;gt; {
  const user = { id: 1, username: 'john_doe' };
  const token = jwt.sign(user, 'secret_key');
  res.json({ token });
});
```
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h4 id=&quot;6-database-integration-the-most-comprehensive-guide&quot;&gt;6️⃣ &lt;strong&gt;Database Integration: The Most Comprehensive Guide&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Node.js and Express.js work seamlessly with databases like &lt;strong&gt;MongoDB&lt;/strong&gt;, &lt;strong&gt;PostgreSQL&lt;/strong&gt;, and &lt;strong&gt;MySQL&lt;/strong&gt;.&lt;/p&gt;
&lt;h5 id=&quot;example-with-mongodb-using-mongoose-&quot;&gt;Example with MongoDB (using Mongoose):&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;Install Mongoose: &lt;code&gt;npm install mongoose&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Connect to MongoDB:&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/myapp', { useNewUrlParser: true, useUnifiedTopology: true });
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;best-practices-for-node-js-amp-express-js-the-most-helpful-tips&quot;&gt;&lt;strong&gt;Best Practices for Node.js &amp;amp; Express.js: The Most Helpful Tips&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Use Environment Variables&lt;/strong&gt;: Store sensitive data like API keys and database credentials in &lt;code&gt;.env&lt;/code&gt; files using the &lt;code&gt;dotenv&lt;/code&gt; package.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimize Performance&lt;/strong&gt;: Use caching mechanisms like Redis to improve performance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Write Clean Code&lt;/strong&gt;: Follow the SOLID principles and modularize your code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test Your Code&lt;/strong&gt;: Use testing frameworks like &lt;strong&gt;Jest&lt;/strong&gt; or &lt;strong&gt;Mocha&lt;/strong&gt; to ensure your application works as expected.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secure Your Application&lt;/strong&gt;: Use HTTPS, sanitize inputs, and implement rate limiting to protect against attacks.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;real-world-applications-of-node-js-amp-express-js-the-most-inspiring-examples&quot;&gt;&lt;strong&gt;Real-World Applications of Node.js &amp;amp; Express.js: The Most Inspiring Examples&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Netflix&lt;/strong&gt;: Uses Node.js for its backend to deliver seamless streaming experiences.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PayPal&lt;/strong&gt;: Leverages Node.js for its payment processing systems.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LinkedIn&lt;/strong&gt;: Built its mobile app backend using Node.js.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;conclusion-the-most-motivating-ending&quot;&gt;&lt;strong&gt;Conclusion: The Most Motivating Ending&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Node.js and Express.js are &lt;strong&gt;powerful tools&lt;/strong&gt; for building modern web applications. With their flexibility, scalability, and vast ecosystem, they empower developers to create anything from simple APIs to complex enterprise-level systems. &lt;/p&gt;
&lt;p&gt;So, what are you waiting for? Dive into the world of Node.js and Express.js, and unleash your creativity! &lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;call-to-action&quot;&gt;&lt;strong&gt;Call to Action&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;If you found this guide helpful, share it with your friends and colleagues! Also, feel free to leave your questions or feedback in the comments below. Happy coding! ✨&lt;/p&gt;
</description>
<category>Setup Node.js</category>
<guid isPermaLink="true">https://coderlegion.com/1299/node-js-express-the-ultimate-comprehensive-and-extremely-detailed-guide-for-developers</guid>
<pubDate>Thu, 27 Feb 2025 10:50:15 +0000</pubDate>
</item>
<item>
<title>Centralized Notifications: Unifying Email, SMS, and FCM Messaging</title>
<link>https://coderlegion.com/355/centralized-notifications-unifying-email-sms-and-fcm-messaging</link>
<description>&lt;div class=&quot;div-blue&quot;&gt;
&lt;span class=&quot;alert-header&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;alert-body&quot;&gt;Imagine having a whole abstraction capable of sending email, SMS, or notification to all your users in one place. Prerequisite: Knowledge of either Javascript, (ExpressJs or Sails Js) and NodeJs.&lt;/span&gt;
&lt;/div&gt;
&lt;h3 id=&quot;abstraction-what-does-it-mean-&quot;&gt;Abstraction, what does it mean?&lt;/h3&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Abstraction_(computer_science)&quot;&gt;Abstraction&lt;/a&gt; is defined as the process of generalizing or concealing specifics of functionality to concentrate attention on more significant information. Another way to put it is as disguising a feature's low-level logic and making it accessible to users through a function or API.&lt;/p&gt;
&lt;p&gt;In programming, whenever you find yourself repeating the same code or logic, it's a good idea to create an abstraction. This is called the &quot;Do not repeat yourself&quot; rule (DRY). This means you take the detailed parts of the code and hide them, making it easier to use and understand. For example, instead of writing the same email-sending code in multiple places, you can create a function that handles it. This way, you only need to call the function whenever you need to send an email. This makes your code cleaner and easier to maintain.&lt;/p&gt;
&lt;h3 id=&quot;firebase-cloud-messaging-feature&quot;&gt;Firebase Cloud Messaging Feature&lt;/h3&gt;
&lt;p&gt;As a case study, a while back at Cudium, I needed to integrate notification into our application so that when a user makes a transaction, they are properly notified about the deposit or withdrawal made on their wallet via email and also via a notification so I decided to integrate with firebase after a few days of research.&lt;/p&gt;
&lt;p&gt;One thing stood out during my research, I needed to support two things :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Cross-platform notification - Android, Web and iOS. All of which Firebase Cloud Messaging (FCM) support&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Support unicast, broadcast and also topic base subscription&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Since all the libraries I use at work are built on top of &lt;a rel=&quot;nofollow&quot; href=&quot;https://expressjs.com/&quot;&gt;Express JS&lt;/a&gt;, they help avoid bootstrapping Express applications from scratch, Sails JS, I mean, which is another form of abstraction. I decided to write a helper file to support this. Think of it as a utility library function, but in Sails JS, it goes beyond that. Let's take a look at it and develop an interesting approach that will help us create a 3-in-1 abstraction for centralized notifications later.&lt;/p&gt;
&lt;div class=&quot;div-blue&quot;&gt;
&lt;span class=&quot;alert-header&quot;&gt;Note:&lt;/span&gt;
&lt;span class=&quot;alert-body&quot;&gt;While the idea is language agnostic, I will be using Javascript to explain.
&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;Let's start with setting up the configuration for the Firebase cloud message which allows us to import all necessary constants&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const fireBaseAdmin = require('firebase-admin');
const {
ttl: TIME_TO_LIVE,
priority: PRIORITY,
backoff: MAX_NOTIFICATION_BACKOFF,
serviceAccount
} = sails.config.custom.constants.fcmNotificationConfig;
// our custom configuration
// this is similar to what we have in place in our env
/*
femNotificationConfig:{
ttl: 60 * 60 * 24,
priority: 'normal',
backoff: 2, // the number of time for retrial for each notification
serviceAccount: process.env.FCM_SERVICE_ACCOUNT 11
}
/

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While the idea is language agnostic, I will be using Javascript to explain.&lt;br&gt;
&lt;/p&gt;&lt;div class=&quot;div-blue&quot;&gt;&lt;br&gt;
&lt;span class=&quot;alert-header&quot;&gt;Note:&lt;/span&gt;&lt;br&gt;
&lt;span class=&quot;alert-body&quot;&gt;All configurations specific to Firebase are extracted from the docs at &lt;a rel=&quot;nofollow&quot; href=&quot;https://firebase.google.com/docs/cloud-messaging&quot;&gt;&lt;em&gt;https://firebase.google.com/docs/cloud-messaging&lt;/em&gt;&lt;/a&gt;. Ensure you have the Firebase admin library installed.&lt;/span&gt;&lt;br&gt;
&lt;/div&gt;
&lt;p&gt;Extracting the configuration is not the only thing needed to set up a Firebase cloud message notification on the backend, you also need to initialize the Firebase itself to be able to use it like so.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const secret Buffer.from(serviceAccount, 'base64').toString(); 
const parsedServiceAccount = JSON.parse(secret);
fireBaseAdmin.initializeApp({
credential: fireBaseAdmin.credential.cert(parsedServiceAccount), });

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sails allows us to specify the type of data we expect to be passed into our functions or controllers but the way, arguments are specified to function in Sails Js is via property specification in this case for us to be able to set Firebase notification as a helper/util library we need to specify some expectation for the argument that the Firebase cloud message library expects to properly send notification across supported platforms such as device token, data, title, body, priority, time to live, topic, mutableContent, contentAvailable e.t.c. To do this, we need to specify the arguments in a table, which are expressed as a JavaScript object following Sail's JS conventions. You can see how they are defined &lt;a rel=&quot;nofollow&quot; href=&quot;https://sailsjs.com/documentation/concepts/helpers#?inputs&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=1733610013582427301&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;div-green&quot;&gt;
&lt;span class=&quot;alert-header&quot;&gt;Tip:&lt;/span&gt;
&lt;span class=&quot;alert-body&quot;&gt;To be able to send notifications to users, I needed to map every user to the assigned device token as shown in the code snippet below but since I am only focused on explaining how to bundle SMS, FCM and email messages as a unit library, I won't be talking about the mapping user to ensure unique messaging even when users change their devices but this snippet below gives a rough idea of what it is.&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://hackmd.io/_uploads/HkcWU8VBR.jpg&quot; alt=&quot;11-temp&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now back from our little detour, I will share the code snippet for the FCM notification and then explain what it does. Let's go.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://hackmd.io/_uploads/HJh0aS4BA.jpg&quot; alt=&quot;Snap (2)&quot;&gt;&lt;/p&gt;
&lt;p&gt;With the parameters specified above, designing a helper function to abstract the implementation details of all things notification was easy.&lt;/p&gt;
&lt;p&gt;First, the implementation starts with a guard clause to ensure all required parameters are provided. If any of &lt;code&gt;topic&lt;/code&gt;, &lt;code&gt;fcmDeviceTokens&lt;/code&gt;, &lt;code&gt;data&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt;, or &lt;code&gt;body&lt;/code&gt; are missing, the function returns &lt;code&gt;false&lt;/code&gt; immediately. Then the &lt;code&gt;payload&lt;/code&gt; , an object was built including the data and notification content (title and body). Default message options (&lt;code&gt;defaultMessageOpts&lt;/code&gt;) such as &lt;code&gt;ttl&lt;/code&gt; (time-to-live) and &lt;code&gt;priority&lt;/code&gt; are set using the provided values or default constants (&lt;code&gt;TIME_TO_LIVE&lt;/code&gt; and &lt;code&gt;PRIORITY&lt;/code&gt;). This is what is passed over to the Firebase notification SDK.&lt;/p&gt;
&lt;p&gt;Secondly, the main focus was on figuring out whether the notification was of one of the three modes listed below :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Unicast&lt;/strong&gt;: Sending a notification to a single device.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Multicast&lt;/strong&gt;: Sending notifications to multiple devices.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Broadcast to Topic&lt;/strong&gt;: Sending notifications to all devices subscribed to a specific topic.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To determine this, the following conditions were used :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Unicast&lt;/strong&gt;: If there's only one device token and no broadcasting topic.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Multicast&lt;/strong&gt;: If there are multiple device tokens and no broadcasting topic.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Broadcast to Topic&lt;/strong&gt;: If a topic is provided along with &lt;code&gt;mutableContent&lt;/code&gt; and &lt;code&gt;contentAvailable&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since sending notifications can fail, I wanted to ensure we retry sending a notification even after a failure. To achieve this, a loop was used to implement a retry mechanism with a backoff strategy, attempting to send the notification up to &lt;code&gt;MAX_NOTIFICATION_BACKOFF&lt;/code&gt; times if not successful.&lt;/p&gt;
&lt;p&gt;For unicast mode, a notification is sent to a single device token. If the response is successful, &lt;code&gt;notified&lt;/code&gt; is set to true; otherwise, a retry attempt message is logged. For multicast mode, notifications are sent to all device tokens using &lt;code&gt;sendEachForMulticast&lt;/code&gt;. The number of successfully sent notifications is logged, and if not all are successful, the failed tokens are identified. The list is compacted to remove null values, and retries are made for the failed tokens. For broadcasting to a topic, notifications are sent to devices registered to the specified topic. If the response contains &lt;code&gt;messageId&lt;/code&gt;, it is marked as successful and &lt;code&gt;notified&lt;/code&gt; is set to true; otherwise, a retry attempt message is logged.&lt;/p&gt;
&lt;p&gt;With this, we have the Firebase utility ready to support all three modes of notification. Now, let's set up the email side of things.&lt;/p&gt;
&lt;h3 id=&quot;email-notification-feature&quot;&gt;Email Notification Feature&lt;/h3&gt;
&lt;p&gt;Many applications need an email support feature, and you can integrate any email service based on your software requirements. For this article, we chose to integrate with Zoho Mail, even though the previous integration was with SendGrid. Therefore, I also had to ensure backward compatibility.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://hackmd.io/_uploads/HJk51UEBC.jpg&quot; alt=&quot;Snap (3)&quot;&gt;&lt;/p&gt;
&lt;p&gt;By listing all the supported email providers in the code above, the application ensures reliable email delivery by using multiple transporters: Zoho Mail and SendGrid. The email payload, combined with a flexible template configuration, allows the application to easily integrate and send well-formatted emails.&lt;/p&gt;
&lt;p&gt;Let's quickly move to SMS notifications before discussing the centralized notification code.&lt;/p&gt;
&lt;h3 id=&quot;sms-notification-feature&quot;&gt;Sms Notification Feature&lt;/h3&gt;
&lt;p&gt;To send notifications, we configure an Axios instance to make HTTP POST requests to the SMS provider. This ensures delivery to users whenever a pin or token is needed, as shown in the code snippet below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://hackmd.io/_uploads/SkiieL4SC.jpg&quot; alt=&quot;temp-4&quot;&gt;&lt;/p&gt;
&lt;p&gt;So with these three utility functions, let's create a centralized notification function that can send notifications via the three channels depending on varying configurations.&lt;/p&gt;
&lt;h3 id=&quot;centralized-notification&quot;&gt;Centralized Notification&lt;/h3&gt;
&lt;p&gt;To centralize SMS, FCM, and Email notifications into a single utility function, we made some basic assumptions. We need to specify the type of notification to send and the necessary payload. This allows us to create a generalized function that builds on the three notification features discussed earlier.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const ALL = 'all';
const EMAIL = 'email';
const PUSH = 'push';
const SMS = 'sms';
const pushTemplate = sails.config.custom.notificationMessage;
const notificationsAllowed = ['sms','email', 'push', 'all'];

module.exports = {
  friendlyName: 'Notification center',
  description: '',
  inputs: {
    templateOption: {
      type: 'ref',
      description: 'An object for providing push and email template option',
      example: { push: 'template-name', email: 'template-name' }
    },
    data: {
      type: 'ref',
      required: true,
      description: 'An object for providing push and email template option',
      example: { push: {}, email: {}, sms:{} }
    },
    notification: {
      type: 'string',
      required: true,
      description: 'notifications to send',
      isIn: notificationsAllowed
    },
    recipientId: {
      type: 'string',
      required: true,
      description: 'User receiving the email or push notification',
    }
  },
  exits: {
    success: {
      description: 'All done.',
    },
  },
  fn: async function ({ templateOption, data, notification, recipientId: userId }) {
    const { sendEmail, mapFcmDeviceToken, sendFcmNotification, parseNotificationMessageTemplate , sendSms } = sails.helpers;
    const user = await User.findOne({ id: userId }).select(['email', 'firstName']);
    const { email, firstName } = user;
    const name = firstName[0].toUpperCase() + firstName.slice(1);

    if (notification === ALL || notification === EMAIL) {
      const template = templateOption[EMAIL];
      const emailData = data[EMAIL];
      if (!template || !emailData) {
        sails.log('Notification-center-email-error:templateOption and data.email is required for sending email');
        return;
      }

      const { subject, from, ...context } = emailData;
      context.name = name;
      const emailSent = await sendEmail.with({ from, options: { to: email, subject, template, context } });
      sails.log('email-sent-success to ', email, 'is', emailSent);
    }

    if (notification === ALL || notification === SMS) {
      const smsData = data[SMS];
      const smsSent = await await sails.helpers.sendSms({
          message: smsData.message
          phoneNumber: smsData.phoneNumber
      });
      sails.log('sms-sent-success to ', smsData.phoneNumber, 'is', smsSent);
    }

    if (notification === ALL || notification === PUSH) {
      const templateType = templateOption[PUSH];
      const tmplData = data[PUSH];
      if (!templateType || !tmplData) {
        sails.log('Notification-center-email-error:templateOption and data.push is required for sending push notification');
        return;
      }

      const useTemplate = pushTemplate[templateType];
      const isMapped = await mapFcmDeviceToken.with({ userId });
      if (isMapped) {
        const { token: fcmDeviceTokens } = isMapped;
        const { body: tmpl, title } = useTemplate;
        const { transactionType, ...contextData} = tmplData;
        contextData.name = name;
        const notificationBody = await parseNotificationMessageTemplate.with({ tmpl, tmplData: contextData });
        const dataPayload = {};
        if(transactionType){
          dataPayload.transactionType = transactionType;
        }
        if(contextData.currency){
          dataPayload.currency = contextData.currency;
        }
        const pushSent =  await sendFcmNotification.with({ fcmDeviceTokens, title, body: notificationBody, data: dataPayload });
        sails.log('push-sent-success to ', fcmDeviceTokens, 'is', pushSent,' Data:', tmplData);
      }
    }
  }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The centralized notification library in the snippet above is designed to send emails, SMS, and push notifications to users. It starts by setting up constants for different notification types and allowed notifications. The required inputs include template options, data for the templates, the type of notification to send, and the recipient's user ID.&lt;/p&gt;
&lt;p&gt;When the function runs, it first gets the user's email and first name using the provided user ID. For email notifications, if the notification type is 'ALL' or 'EMAIL', it creates the email using the given template and data, sends it, and logs the result. Similarly, for SMS notifications, it checks if the type is 'ALL' or 'SMS', and sends an SMS with the provided data.&lt;/p&gt;
&lt;p&gt;For push notifications, if the type is 'ALL' or 'PUSH', it retrieves the appropriate template and data, maps the user's FCM device tokens, and constructs the push notification message. The message is then sent.&lt;/p&gt;
&lt;p&gt;The library ensures each notification type is only attempted if the required data and templates are provided, and logs errors if any data is missing. It uses necessary helper functions to handle the sending process, ensuring a robust and flexible notification system. This approach guarantees that notifications are reliably sent through multiple channels, enhancing user communication.&lt;/p&gt;
&lt;p&gt;While this may seem like a lengthy process for sending notifications, emails, or SMS to users, it ensures that each component can work independently and also together.&lt;/p&gt;
&lt;p&gt;One example of how this can be applied is during the KYC process for a new user, as shown in the snippet below.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;await sails.helpers.notificationCenter.with({ notification: 'all', recipientId: id,
    data: {
        push : { updateProfile: true },
        email : { subject,from: mailFrom }
    },
    templateOption: {
        push: kycCompletedSuccessfully ? USER_ID_VERIFICATION : USER_ID_VERIFICATION_FAILED ,
        email: template
    }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A similar use case could be for sending out transaction notifications to users after a debit or credit process.&lt;/p&gt;
&lt;p&gt;While I know this article does not really give you the in-depth step-by-step process of writing this whole logic out. I hope that it gives you an idea of how you can set up all notification-related logic in one library and use it at will.&lt;/p&gt;
&lt;p&gt;In summary, I hope this article lays in your heart the importance of abstraction using function and how to build better software with it&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://sailsjs.com/&quot;&gt;https://sailsjs.com&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Abstraction_(computer_science)&quot;&gt;https://en.wikipedia.org/wiki/Abstraction_(computer_science)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://firebase.google.com/docs/cloud-messaging&quot;&gt;https://firebase.google.com/docs/cloud-messaging&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;I am Caleb and you can reach me at&lt;/em&gt;&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/adewole-caleb&quot;&gt;&lt;strong&gt;&lt;em&gt;Linkedin&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;&lt;em&gt;or follow me on&lt;/em&gt;&lt;/strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://x.com/Soundboax&quot;&gt;&lt;strong&gt;&lt;em&gt;Twitter&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/355/centralized-notifications-unifying-email-sms-and-fcm-messaging</guid>
<pubDate>Sun, 16 Jun 2024 12:04:27 +0000</pubDate>
</item>
</channel>
</rss>