Tag: Javascript

  • React Native vs Flutter: Which One Should You Pick in 2026?

    React Native vs Flutter: Which One Should You Pick in 2026?

    If you are planning a mobile app, you have probably heard the same question over and over. Should you use React Native or Flutter? I have built apps with both, and I can tell you there is no single winner. The better choice depends on your team, your budget, and what your app needs to do.

    In this post I will explain both tools in simple words, compare them on the things that matter, and help you choose without the confusion.

    What Is React Native?

    React Native is a framework made by Meta (the company behind Facebook). It lets you build iOS and Android apps using JavaScript or TypeScript. If you already know React for the web, you will feel at home very quickly.

    The big idea is simple. You write your app once, and React Native turns your code into real native parts on each phone. A button on iPhone looks like an iPhone button, and a button on Android looks like an Android button.

    Many well known apps use it, including parts of Instagram, Shopify, and Discord.

    What Is Flutter?

    Flutter is a framework made by Google. It uses a language called Dart, which is easy to pick up if you know Java, C#, or JavaScript.

    Flutter works in a different way. It does not use the native buttons of the phone. Instead, it draws every pixel on the screen by itself using its own engine. This gives you full control over how your app looks, and it means your app looks the same on every device.

    Apps like Google Pay, BMW’s app, and Alibaba’s Xianyu use Flutter.

    Language and Learning Curve

    This is where many people make their choice.

    React Native uses JavaScript or TypeScript. These are some of the most popular languages in the world. If your team builds websites already, they can start building mobile apps with very little training.

    Flutter uses Dart. Dart is clean and friendly, but fewer people know it. Most developers learn it in a week or two, so it is not a big wall. Still, it is one more thing to learn.

    If your team is full of web developers, React Native feels easier. If you are starting fresh or hiring new people, Flutter is just as easy to learn.

    Performance

    Let me be honest here. For most apps, both are fast enough, and your users will not notice a difference.

    Flutter has a small edge in smooth animations and heavy visual screens. Because it draws everything itself, it can keep a steady frame rate even with complex designs. Its newer rendering engine has made this even better.

    React Native used to have a problem with a slow bridge between JavaScript and the native side. The newer architecture has fixed most of this. Today, React Native feels quick for normal business apps, shopping apps, social apps, and booking apps.

    If you are building a game or an app with very rich animations, lean toward Flutter. For almost everything else, performance should not decide this for you.

    User Interface and Design

    Flutter gives you a lot of freedom. You can build almost any design you imagine, and it will look the same on iOS, Android, and even the web. Designers love this.

    React Native uses the real native components of each platform, so your app feels natural to users. The downside is that sometimes you need extra work to make things look the same on both phones.

    Here is a simple way to think about it. If you want a custom, branded look that stays identical everywhere, Flutter is great. If you want the app to feel like it belongs on each phone, React Native is great.

    Development Speed

    Both tools have hot reload. This means you change your code and see the result on the screen in a second, without restarting the app. It saves a huge amount of time.

    Flutter comes with many things built in, like buttons, menus, and animations. You can build a nice looking app fast without adding many outside packages.

    React Native gives you more choices, but that also means more decisions. You will pick your own tools for navigation, state management, and styling. This can be good if you like freedom, and tiring if you do not.

    In my own work, I find Flutter faster for building a brand new app with custom screens. I find React Native faster when I am adding mobile to an existing React project.

    Community and Libraries

    React Native has been around a bit longer and has a very large community. Since it is built on JavaScript, you can use a huge number of packages from the npm world. You will almost always find a library for what you need.

    Flutter’s community is growing fast. The official package site has thousands of packages, and many are of good quality. Google also supports it strongly, so you get good docs and regular updates.

    If you ever get stuck, you will find answers for both. React Native has more old answers online. Flutter has cleaner official guides.

    Hiring and Cost

    This part matters a lot for business owners.

    There are more JavaScript developers in the world than Dart developers. That means finding React Native developers is usually easier, and you often have more choices on price.

    Flutter developers are fewer, but many of them are very focused on mobile work and do it well. In some markets, Flutter developers cost about the same as React Native developers.

    Either way, building one app for two platforms saves a lot of money compared to building two separate native apps. That is the real win, no matter which one you pick.

    Beyond Mobile

    Both tools are growing past phones.

    Flutter supports iOS, Android, web, Windows, macOS, and Linux from one codebase. The mobile side is very strong, and the others keep getting better.

    React Native focuses mostly on mobile, but you can use related tools to reach the web and desktop. If you want to share a lot of code between a React website and a mobile app, React Native gives you a natural path.

    Testing and Maintenance

    Both frameworks have good testing tools. Flutter has testing built in from the start, which makes it easy to test screens and logic.

    For long term maintenance, one thing to watch in React Native is package updates. Sometimes a library stops working after a big update, and you need to fix it. Flutter updates tend to be smoother because more things come from one place.

    This is not a deal breaker for either one. It just means you should plan time for updates, no matter what you choose.

    When to Choose React Native

    React Native is a good fit when:

    1. Your team already knows JavaScript or React.
    2. You want to share code with a web app built in React.
    3. You want the app to feel fully native on each platform.
    4. You need to hire quickly from a large pool of developers.
    5. You are adding mobile features to an existing JavaScript project.

    When to Choose Flutter

    Flutter is a good fit when:

    1. You want a very custom design that looks the same everywhere.
    2. Your app has many animations or rich visuals.
    3. You are starting from zero and do not have a strong language preference.
    4. You want one codebase for mobile, web, and desktop.
    5. You like having most tools built in and ready to use.

    My Honest Advice

    After years of building apps, here is what I tell my clients. Do not pick a framework because it is trending on social media. Pick it based on your team and your goals.

    If your people already write JavaScript every day, go with React Native. You will move faster and spend less on training. If you care most about design, smooth motion, and one look on every screen, go with Flutter.

    And remember, a good developer matters more than a good framework. A well built app in either tool will beat a messy app in the other. Spend your energy on clear planning, simple features, and a smooth experience for your users.

    Final Thoughts

    React Native and Flutter are both strong, mature, and trusted by big companies. You cannot make a terrible choice here. The best way to decide is to list your needs, look at your team, and build a small test screen in both if you have time. You will learn more in two days of testing than in two weeks of reading opinions.

    If you are still not sure which one fits your project, feel free to reach out. I am happy to look at your idea and help you pick the right path before you spend any money.

  • How to Set Up Auth for WordPress GraphQL in a Next.js Project

    How to Set Up Auth for WordPress GraphQL in a Next.js Project

    If you use WordPress as a headless CMS with Next.js, sooner or later you will need to fetch private data. Maybe it is draft posts, a preview page, or content only logged in users should see. Public data works without any login, but private data needs authentication.

    In this guide I will show you how to set up auth for WPGraphQL and how to use your credentials safely inside Next.js. I will keep it simple and go one step at a time.

    What You Need Before You Start

    1. A WordPress site running on HTTPS
    2. The WPGraphQL plugin installed and active
    3. A Next.js project (App Router in this guide)
    4. Basic knowledge of fetch and environment variables

    Step 1: Install and Check WPGraphQL

    In your WordPress dashboard, go to Plugins, then Add New, and search for WPGraphQL. Install it and click Activate.

    After that, you will see a new GraphQL menu in the sidebar. Your endpoint will be:

    https://yourdomain.com/graphql

    Open the GraphiQL IDE from the GraphQL menu and run a simple query to confirm everything works.

    Step 2: Create an Application Password

    This is built into WordPress. It is the easiest choice when your Next.js server needs to talk to WordPress, for example to read drafts. Think of it as your API key.

    1. In WordPress, go to Users, then Profile
    2. Scroll down to the Application Passwords section
    3. Type a name like “Nextjs Frontend”
    4. Click Add New Application Password
    5. Copy the password right away. WordPress shows it only once

    A tip from experience: create a separate user with only the role it needs, like Editor, instead of using your admin account. If the key ever leaks, the damage stays small.

    Step 4: Store the Credentials in Environment Variables

    In the root of your Next.js project, open the .env.local file and add:

    WORDPRESS_GRAPHQL_URL=https://yourdomain.com/graphql
    WP_APP_USER=your_wordpress_username
    WP_APP_PASSWORD=abcd efgh ijkl mnop qrst uvwx

    Very important: do not add the NEXT_PUBLIC_ prefix to these names. Any variable with that prefix gets sent to the browser, and anyone can see it. Without the prefix, the value stays on the server only.

    Also make sure .env.local is listed in your .gitignore file so it never goes to GitHub.

    Step 5: Create a Helper Function for API Calls

    Instead of writing the same fetch code again and again, make one helper. Create a file at lib/wp.js:

    export async function wpFetch<T>(query: string): Promise<T> {
        const token = btoa(`${process.env.WP_USER}:${process.env.WP_APP_PASSWORD}`);
    
        const res = await fetch(process.env.WP_GRAPHQL_URL!, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                Authorization: `Basic ${token}`,
            },
            body: JSON.stringify({ query }),
            next: { revalidate: 60 }, 
        });
    
        const json = await res.json();
        if (json.errors) throw new Error(JSON.stringify(json.errors));
        return json.data;
    }

    Here is what this code does in simple words:

    1. It joins your username and application password, and turns them into a Base64 string.
    2. It sends that string in the Authorization header with the word Basic in front.
    3. WordPress reads the header, checks the user, and then returns data based on that user’s permissions.
    4. The revalidate: 60 line tells Next.js to refresh the cached data every 60 seconds.

    Step 6: Use the Helper in a Page

    Now let us fetch draft posts, which only logged in users can see. Create app/drafts/page.js:

    import { wpFetch } from "@/lib/wp";
    
    const QUERY = `
      query GetDrafts {
        posts(where: { status: DRAFT }) {
          nodes {
            id
            title
            slug
          }
        }
      }
    `;
    
    export default async function DraftsPage() {
      const data = await wpFetch(QUERY);
      const posts = data.posts.nodes;
    
      return (
        <main>
          <h1>Draft Posts</h1>
          {posts.map((post) => (
            <p key={post.id}>{post.title}</p>
          ))}
        </main>
      );
    }

    This is a Server Component, so the API call runs on the server. Your password never reaches the browser. If you run the same query without the auth header, WordPress returns an empty list. With the header, you see your drafts. That is how you know auth is working.

    Common Mistakes to Avoid

    1. Using NEXT_PUBLIC_ on secret values
    2. Using an admin account when an Editor account is enough
    3. Running your site on HTTP. Application Passwords need HTTPS on live sites
    4. Forgetting to add .env.local to .gitignore
    5. Calling private queries from client components, which exposes your keys

    Final Thoughts

    Setting up auth for WordPress GraphQL is not hard once you know the flow. Create an application password, keep it in a server only environment variable, send it in the Authorization header, and always make private calls from the server side of Next.js. If you need user logins, add JWT on top.

    I hope this guide saved you some time. If you get stuck on any step, leave a comment and I will try to help.

  • Why Cookies Stop Working After You Deploy a MERN App (And How to Fix It)

    Why Cookies Stop Working After You Deploy a MERN App (And How to Fix It)

    You build a login system. It works perfectly on your laptop. Then you deploy the React app to Vercel and the Express API to Render, and suddenly nobody can stay logged in. The login request returns 200, but the next request returns 401.

    If your MERN cookies are not working in production, you are not alone. This is one of the most common problems after a first deployment. The good news is that it always comes down to a few settings. In this post I will show you why it happens and how I fix it step by step.

    Why cookies work on localhost but not in production

    On your machine, the frontend runs on localhost:5173 and the backend runs on localhost:5000. Browsers do not care about ports when they decide if two URLs are on the same site. So both are the same site, and cookies flow freely.

    After deployment, your URLs look like this:

    • Frontend: myapp.vercel.app
    • Backend: myapp-api.onrender.com

    These are two different sites. Both vercel.app and onrender.com are on the public suffix list, so every project under them counts as its own site. Now your API cookie is a third-party cookie, and browsers treat it very differently.

    How to check what is broken

    Before you change any code, open DevTools and look at two things:

    1. Go to the Network tab, click your login request, and check the response headers for Set-Cookie.
    2. Go to the Application tab, open Cookies, and see if your cookie was saved.

    Here is how to read what you see:

    What you seeLikely cause
    No Set-Cookie header at allWrong secure setting, missing trust proxy, or a CORS block
    Set-Cookie has a warning iconSameSite=None is used without Secure, or the browser blocked it
    Cookie is saved but not sent on the next requestwithCredentials is missing on the client
    CORS error that mentions credentialsWildcard origin or credentials: true is missing

    Step 1: Set the right cookie options on the server

    For a cross-site setup, your cookie needs HttpOnly, Secure and SameSite=None. I keep these in one file so login and logout always use the same values.

    // utils/cookieOptions.js
    const isProd = process.env.NODE_ENV === "production";
    
    export const authCookieOptions = {
      httpOnly: true,
      secure: isProd,
      sameSite: isProd ? "none" : "lax",
      path: "/",
      maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
    };

    Browsers reject SameSite=None if the cookie is not also Secure. That is why I only turn it on in production. On localhost over plain HTTP, lax works fine.

    Also make sure NODE_ENV is really set to production on your hosting dashboard. If it is missing, your app will silently use the development values.

    Now use the options in your login controller:

    import jwt from "jsonwebtoken";
    import { authCookieOptions } from "../utils/cookieOptions.js";
    
    export const login = async (req, res) => {
      // check email and password first, then:
      const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, {
        expiresIn: "7d",
      });
    
      res.cookie("token", token, authCookieOptions);
      res.status(200).json({ message: "Login successful" });
    };

    If you still need to set up your database layer, read my guide on how to connect MongoDB with Node.js using Mongoose.

    Step 2: Tell Express to trust the proxy

    Render, Railway and most hosts put a proxy in front of your app. The proxy handles HTTPS, then passes plain HTTP to Express. Because of this, Express may think the request is not secure, and libraries like express-session will refuse to set a secure cookie.

    Fix it with one line, placed before your other middleware:

    app.set("trust proxy", 1);

    This tells Express to read the X-Forwarded-Proto header from the proxy, so req.secure becomes true.

    Step 3: Set up CORS with credentials

    Your API must allow your exact frontend origin and allow credentials. This is the full server setup:

    import express from "express";
    import cors from "cors";
    import cookieParser from "cookie-parser";
    
    const app = express();
    
    app.set("trust proxy", 1);
    
    app.use(
      cors({
        origin: process.env.CLIENT_URL, // https://myapp.vercel.app
        credentials: true,
      })
    );
    
    app.use(express.json());
    app.use(cookieParser());

    These are the mistakes I see most often:

    • Using origin: "*" together with credentials: true. Browsers reject this.
    • Adding a trailing slash, like https://myapp.vercel.app/. The origin must match exactly.
    • Forgetting to add CLIENT_URL in the hosting dashboard.
    • Placing cors() after your routes instead of before them.

    Step 4: Send credentials from the frontend

    Even if the server is perfect, the browser will not send cookies unless you ask it to. With Axios, set withCredentials once on your instance:

    // src/lib/api.js
    import axios from "axios";
    
    export const api = axios.create({
      baseURL: import.meta.env.VITE_API_URL,
      withCredentials: true,
    });

    With the Fetch API, use credentials: "include":

    fetch(`${import.meta.env.VITE_API_URL}/auth/me`, {
      credentials: "include",
    });

    If you are still on Create React App and import.meta.env does not work for you, see my guide on migrating a MERN app from CRA to Vite.

    Step 5: Clear the cookie on logout the right way

    A cookie is only removed if the clear options match the options used to set it. Do not pass maxAge when clearing, because it can stop the cookie from being deleted.

    import { authCookieOptions } from "../utils/cookieOptions.js";
    
    export const logout = (req, res) => {
      const { maxAge, ...clearOptions } = authCookieOptions;
    
      res.clearCookie("token", clearOptions);
      res.status(200).json({ message: "Logged out" });
    };

    The problem that config alone cannot fix: blocked third-party cookies

    You can do everything above and still see broken logins for some users. Safari blocks third-party cookies by default. Firefox isolates them per site. Chrome blocks them in Incognito mode and when users turn on the setting.

    So a cross-site cookie setup works for you in Chrome, then fails for a client on an iPhone. This is why I do not rely on cross-site cookies for real projects. There are three better options.

    Option 1: Use one domain for both apps (best option)

    Put your frontend and backend on subdomains of the same domain:

    • app.yourdomain.com for the frontend
    • api.yourdomain.com for the backend

    These are the same site, so the browser treats your cookie as first-party. You can then use sameSite: "lax" and it works in every browser. Both Vercel and Render support custom domains.

    Option 2: Proxy API calls through Vercel

    If you cannot use a custom domain yet, use Vercel rewrites. The browser talks only to your Vercel domain, and Vercel forwards the request to your API. Add this to vercel.json:

    {
      "rewrites": [
        {
          "source": "/api/:path*",
          "destination": "https://myapp-api.onrender.com/api/:path*"
        }
      ]
    }

    Then set VITE_API_URL=/api in your frontend environment. Every request now looks same-origin, and the cookie becomes first-party. One thing to know: this works for normal REST calls, but WebSocket connections need to go straight to the backend, so real-time features can still run into cookie problems.

    Option 3: Add the Partitioned attribute (CHIPS)

    CHIPS (Cookies Having Independent Partitioned State) lets a cross-site cookie work in supporting browsers, as long as it is stored per top-level site. You add one option:

    res.cookie("token", token, {
      ...authCookieOptions,
      partitioned: true,
    });

    It needs secure: true and sameSite: "none", and you should use a recent Express version. It mainly helps Chromium browsers, and it does not solve the Safari problem. I treat it as a backup, not the main fix.

    Do not forget CSRF

    When you use SameSite=None, you lose some of the built-in protection against CSRF attacks. If your app changes data with cookie auth, add a CSRF token, or at least check the Origin header on write requests. Using a same-site setup like Option 1 avoids this problem entirely.

    Quick checklist

    Run through this list before you spend hours debugging:

    • NODE_ENV is set to production on the server
    • Cookie has httpOnly: true, secure: true and sameSite: "none" in production
    • app.set("trust proxy", 1) is added before other middleware
    • CORS uses the exact frontend URL, with no trailing slash and no *
    • CORS has credentials: true
    • Axios has withCredentials: true, or Fetch has credentials: "include"
    • Logout uses the same cookie options, without maxAge
    • Tested in Safari and in a Chrome Incognito window
    • Considered a custom domain or a Vercel proxy

    If your auth check runs through TanStack Query and you see stale data after login or logout, my post on common React Query issues and invalidate queries will help.

    Frequently Asked Questions

    Why do my cookies work in Postman but not in the browser?

    Postman does not enforce SameSite rules or CORS. It only proves your API logic works. Browsers add extra security checks, so a request can pass in Postman and still fail in the browser.

    Should I use localStorage instead of cookies?

    It avoids the cross-site problem, but any script on your page can read localStorage, so an XSS bug can leak the token. An HttpOnly cookie is safer for auth tokens. I prefer to fix the cookie setup instead of moving the token to localStorage.

    Does SameSite=Strict work when the frontend and backend are on different domains?

    No. Strict blocks the cookie on all cross-site requests. For separate domains you need None, or better, move both apps under one parent domain and use Lax.

    Why does my cookie disappear only on iPhones?

    Safari blocks third-party cookies by default. The fix is to make your API same-site with a custom domain or a proxy, not to change cookie flags.

    Final thoughts

    Most MERN cookie problems in production come from one idea: on localhost your apps share a site, and after deployment they do not. Set the correct cookie flags, trust the proxy, configure CORS with credentials, and send credentials from the client. Then, for a setup that works in every browser, move your frontend and API under the same domain.

    If you want help with authentication, deployment or a full build, take a look at my MERN stack development service and Node.js backend development service. You can also see my past projects, read more on the blog, or contact me directly.

  • Sending Push Notifications from Express.js with Firebase Admin SDK

    Sending Push Notifications from Express.js with Firebase Admin SDK

    A practical guide on setting up your Express.js backend to send push notifications through Firebase Cloud Messaging, from the admin SDK setup to writing the actual send function.

    If you read my last post on setting up Firebase Cloud Messaging in React Native, you already know how to get a device ready to receive notifications. But that is only one side of the equation. A phone sitting there with a token isn’t going to notify anyone by itself. Something has to actually send the message. That something, in most of my projects, is an Express.js backend.

    This part is honestly a lot less painful than the mobile side. There’s no Gradle, no missing JSON files, no red screen of death. Once you have your service account key from Firebase, you’re basically 15 minutes away from sending your first test notification.

    What you need before you start

    Before writing any code, go to your Firebase Console, open your project settings, and go to the Service Accounts tab. From there, generate a new private key. This downloads a JSON file with your credentials. Do not commit this file to git. I’ve seen people push it by accident and it’s not a fun cleanup.

    You’ll need three values out of that file:

    • project_id

    I keep all three in a .env file rather than reading the JSON file directly, mostly because it plays nicer with how I deploy things. If you’re on a platform like Render or Railway, you’ll be pasting these into environment variables anyway, so it’s the same workflow either way.

    Installing the admin SDK

    You only need one package for this:

    npm install firebase-admin

    No extra dependencies, no messing around with different SDKs for different platforms. The same firebase-admin package handles Android and iOS tokens.

    Initializing the app

    This part trips people up more than it should, mostly because of one small detail: the private key. When you copy it from the .env file, the line breaks get turned into the literal characters \n instead of actual newlines. If you pass it to Firebase like that, initialization will fail with a vague error about invalid credentials. The fix is a simple .replace() call.

    Here’s how I set it up:

    javascript

    require("dotenv").config();
    var admin = require("firebase-admin");
    
    admin.initializeApp({
      credential: admin.credential.cert({
        projectId: process.env.PROJECT_ID,
        privateKey: process.env.PRIVATE_KEY?.replace(/\\n/g, "\n"),
        clientEmail: process.env.CLIENT_EMAIL,
      }),
    });

    That ?.replace(/\\n/g, “\n”) line is doing the real work here. Without it, you’ll spend way more time debugging than the actual problem deserves. I learned this one the hard way after staring at a “failed to parse private key” error for way longer than I’d like to admit.

    Writing the send function

    Once the app is initialized, sending a notification is just a matter of building a message object and calling admin.messaging().send(). The message needs a notification object with a title and body, an optional data object for anything extra you want the app to handle on its own, and the device token you got from the frontend.

    javascript

    const sendTestPushNotification = async ({ title, body, data, fcm_token }) => {
      try {
        const message = {
          notification: {
            title,
            body,
          },
          data: data,
          token: fcm_token,
        };
    
        await admin.messaging().send(message);
      } catch (error) {}
    };

    A few things worth pointing out here. The data field is separate from notification on purpose. Anything inside notification is what shows up in the notification tray automatically, handled by the OS. The data field is for values your app needs internally, like a screen to navigate to or an ID to fetch. Also, data values have to be strings. If you try passing a number or an object directly, Firebase will throw an error, so stringify anything that isn’t already a string.

    The fcm_token is the piece that connects this whole thing back to the React Native setup. Whatever device you want to notify, that’s the token you save from that side and pass in here.

    Where this token actually comes from

    In a real app, you’re not going to hardcode a token for testing forever. The usual flow looks like this: the app requests the FCM token on launch, sends it to your backend, and your backend saves it against that user in the database. Then whenever you need to notify that user, you just look up their saved token and call the send function above.

    One thing to keep in mind, and I mentioned this in the React Native post too, tokens are not permanent. They refresh, sometimes for no obvious reason. So don’t just save it once and forget about it. Make sure your app updates the saved token whenever it changes, otherwise you’ll end up sending notifications into a void and wondering why your open rates dropped.

    A basic error handling note

    The example above has an empty catch block, which is fine for quick testing but you shouldn’t ship it like that. In a real backend, you’d want to at least log the error, and ideally check if the failure was because the token is no longer registered. Firebase will tell you when a token is invalid or expired, and that’s your cue to remove it from your database so you’re not repeatedly trying to send to a dead address.

    Wrapping up

    That’s really the whole loop. The React Native side handles getting permission and grabbing a token. The Express.js side takes that token and turns it into an actual notification on someone’s phone. Neither half is particularly complicated once you’ve done it once, but the small details, like that private key formatting, are usually what eat up your time the first time around.

    If you haven’t already, it’s worth reading through the React Native setup guide first if you’re building this end to end. Once both pieces are in place, sending notifications becomes a pretty small function call away.

  • How to Connect MongoDB with Node.js using Mongoose

    How to Connect MongoDB with Node.js using Mongoose

    If you’re building a backend app with Node.js, at some point you’ll need a database to store your data. MongoDB is one of the most popular choices for this, especially when you’re working with JavaScript on both the frontend and backend. But connecting MongoDB directly with Node.js can get messy. That’s where Mongoose comes in.

    Mongoose is a library that sits between your Node.js app and MongoDB. It makes things much easier by giving you a simple way to define your data structure and talk to the database. In this post, I’ll walk you through the whole process step by step.

    What is Mongoose

    Mongoose is an ODM, which stands for Object Data Modeling. In simple words, it lets you create models for your data (like a User model or a Post model) and it handles all the communication with MongoDB behind the scenes. Instead of writing raw database queries, you get clean and readable functions.


    Step 1: Set Up Your Project

    First, create a new folder for your project and open it in your terminal. Then run this command to start a Node.js project:

    This will create a package.json file for you.

    Step 2: Install Mongoose

    Now install Mongoose using npm:

    That’s it. No extra setup needed. Mongoose is ready to use once it’s installed.

    Step 3: Get Your MongoDB Connection String

    Before connecting, you need a MongoDB database. You have two options here:

    • Use MongoDB Atlas (cloud version, free tier available)
    • Use MongoDB locally on your computer

    If you’re just starting out, MongoDB Atlas is the easier option. Just create a free account, set up a cluster, and copy your connection string. It will look something like this:

    Step 4: Connect to MongoDB

    Now create a file called server.js (or index.js, whatever you prefer) and write this code:

    Replace your-connection-string-here with the actual connection string from your MongoDB Atlas dashboard. Run this file with node server.js and if everything is correct, you’ll see “Connected to MongoDB” printed in your terminal.

    Step 5: Create a Schema and Model

    This is where Mongoose really shines. A schema defines the shape of your data. Let’s say we’re building a simple user system:

    The schema tells Mongoose what fields your data will have and what type each field should be. The model is what you’ll actually use to create, read, update, and delete data.

    Step 6: Save Data to the Database

    Once your model is ready, you can save new data like this:

    Step 7: Fetch Data from the Database

    To get data back, you can use the find method:

    This will return all the users stored in your database.

    Common Mistakes to Avoid

    While connecting MongoDB with Node.js, beginners often face a few issues. Here are some things to watch out for:

    • Make sure your IP address is whitelisted in MongoDB Atlas, otherwise the connection will fail
    • Double check your username and password in the connection string
    • Don’t forget to handle errors using try-catch or .catch(), so your app doesn’t crash silently
    • Keep your connection string in a .env file instead of writing it directly in your code, for better security

  • Getting Started with Firebase Cloud Messaging in React Native

    Getting Started with Firebase Cloud Messaging in React Native

    The first hurdle is always the config

    I still remember the first time I tried to add push notifications to a React Native project. It was about four years ago, and I thought it would be a quick afternoon task. I was very wrong. Between the Gradle sync errors and the missing JSON files, I spent way more time debugging my environment than actually writing code. But honestly, once you get the foundation right, the rest of the MERN stack integration feels like a breeze.

    The setup you actually need

    Before you even touch your Javascript files, you have to get the native side talking to Firebase. You’ll need to head over to the Firebase Console, create a project, and download that google-services.json for Android. It goes into your android/app folder. If you miss this, the app will just crash on startup with a cryptic error that doesn’t tell you much. For the library, I always stick with @react-native-firebase/app and @react-native-firebase/messaging. They are the industry standard for a reason.

    // First, install the core and messaging modules
    // npm install @react-native-firebase/app @react-native-firebase/messaging
    
    import messaging from '@react-native-firebase/messaging';
    
    async function requestUserPermission() {
      const authStatus = await messaging().requestPermission();
      const enabled =
        authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
        authStatus === messaging.AuthorizationStatus.PROVISIONAL;
    
      if (enabled) {
        console.log('Authorization status:', authStatus);
      }
    }

    Getting that device token

    The most important part of this whole process is getting the FCM token. Think of this as the mailing address for that specific phone. You’ll want to grab this token and send it to your backend. In my MERN setups, I usually store this in a User schema in MongoDB. But here is a tip I learned the hard way. Tokens can change. They expire or get refreshed when an app is restored on a new device. So you should always listen for token refreshes using the onTokenRefresh method. If you don’t do this, you’ll end up sending notifications to dead addresses, and your delivery rates will tank.

    Setting this up is basically the rite of passage for mobile developers. It feels tedious when you’re doing it, but seeing that first notification pop up on your physical device makes it all worth it. Don’t let the initial setup frustrate you. We’ve all been there, staring at a red screen of death because of a typo in the build.gradle file. Stick with it, and once the bridge is built, the real fun begins.

    Now that your app can receive notifications, the next step is actually sending them. I wrote a separate guide on setting up the backend side with Express.js and Firebase Admin SDK, covering the service account setup and a working send function. You can read it here if you want to complete the full loop.

    Hasnain Alam, Full-Stack Developer | hasnainalam.com