Blog

Taming the API Chaos: Unifying Your Node.js Feeds with the Factory Pattern

Taming the API Chaos: Unifying Your Node.js Feeds with the Factory Pattern

Hey there! ๐Ÿ‘‹ I want you to picture a scenario.

You are building the "Home Feed" for a massive social platformโ€”let's call it Global Diary. The product manager runs into the room and says, "Okay, on the main screen, we need to show EVERYTHING. User posts, breaking news, upcoming events, and marketplace listings, all mixed together in one infinite scroll."

You look at your database. It's a mess.

  • Posts have a description and media array.
  • Events hide their dates inside a nested event object.
  • Marketplace items have prices and locations.
  • News has a source link and status.

If you send this raw data to your frontend team, they are going to hate you. Their React/Angular components will be littered with if (item.event) { ... } else if (item.company) { ... }.

There is a better way. Let's talk about the Factory Method Design Pattern.

Understanding the Factory Pattern

What is the Factory Pattern?

The Factory Pattern is a creational design pattern that provides an interface for creating objects without specifying their exact classes. In simpler terms, it's like having a smart factory that looks at raw materials (your messy data) and produces standardized products (clean, consistent objects).

Think of it like this: You have a factory that receives different types of raw materialsโ€”wood, metal, plasticโ€”and produces standardized furniture pieces. The factory knows how to handle each material type and transform it into the final product.

Why is it Useful?

Before we dive into implementation, let's understand why this pattern is so powerful:

  1. Consistency: It ensures all your data follows the same structure, regardless of its source
  2. Maintainability: Changes to data structure only need to be fixed in one place
  3. Scalability: Adding new data types is as simple as adding a new case to your factory
  4. Separation of Concerns: Your business logic stays separate from data transformation logic
  5. Frontend Simplicity: Your frontend developers can write code assuming a consistent data structure

The "Yard Sale" Problem

First, let's look at the problem. Based on the data structure you might see in a complex app like Global Diary, your raw API response usually looks like a yard saleโ€”a bunch of different things thrown together without a common shape.

Look at this raw Post object:

{
  "id": "post-123",
  "contentType": "posts",
  "description": "<p>Just posted a photo!</p>",
  "user": { "name": "Jane Doe", "username": "jane-d" },
  "media": [{ "url": "image.jpg", "type": "image" }],
  "likesCount": 45
}

Now, look at this Event object:

{
  "id": "event-999",
  "contentType": "event",
  "title": "Summer Music Festival",
  "event": {
    "startDate": "2026-01-12",
    "actionType": "buy_ticket",
    "actionTarget": "https://ticket-link.com"
  },
  "user": { "name": "Event Organizer Ltd" }
}

Question for you: How many times have you written a frontend component that breaks because it tried to access item.description but the object was actually an event that uses item.event.description?

The Solution: The Factory Transformer

Alright, classic use-case ๐Ÿ‘Œ โ€” notifications are perfect for Factory Pattern.

We are going to write a Node.js Class that acts as a translator. Instead of littering your code with if/else or switch statements everywhere, you want to do this:

sendNotification("email", "Hello");
sendNotification("sms", "Hello");
sendNotification("push", "Hello");

๐Ÿ‘‰ without conditional logic scattered throughout your codebase.

This is the Factory Method. It manufactures the right notification object based on the type, keeping your business logic clean and unaware of implementations.

Step 1: Base Notification Interface (Conceptual)

JavaScript doesn't enforce interfaces, but we define a base class for consistency:

Step 2: The Implementation

Let's write this in Node.js. We will create a Notification Factory:

class Notification {
  send(message) {
    throw new Error("send() must be implemented");
  }
}
 
module.exports = Notification;

Concrete Notification Classes

Email Notification

const Notification = require("./Notification");
 
class EmailNotification extends Notification {
  send(message) {
    console.log(`๐Ÿ“ง Email sent: ${message}`);
  }
}
 
module.exports = EmailNotification;

SMS Notification

const Notification = require("./Notification");
 
class SMSNotification extends Notification {
  send(message) {
    console.log(`๐Ÿ“ฑ SMS sent: ${message}`);
  }
}
 
module.exports = SMSNotification;

Push Notification

const Notification = require("./Notification");
 
class PushNotification extends Notification {
  send(message) {
    console.log(`๐Ÿ”” Push notification sent: ${message}`);
  }
}
 
module.exports = PushNotification;

Notification Factory (Core Part)

This is where the Factory Pattern lives ๐Ÿ‘‡

const EmailNotification = require("./EmailNotification");
const SMSNotification = require("./SMSNotification");
const PushNotification = require("./PushNotification");
 
class NotificationFactory {
  static create(type) {
    switch (type) {
      case "email":
        return new EmailNotification();
      case "sms":
        return new SMSNotification();
      case "push":
        return new PushNotification();
      default:
        throw new Error("Invalid notification type");
    }
  }
}
 
module.exports = NotificationFactory;

Usage Example

Your business logic stays clean and unaware of implementations:

const NotificationFactory = require("./NotificationFactory");
 
function sendNotification(type, message) {
  const notification = NotificationFactory.create(type);
  notification.send(message);
}
 
sendNotification("email", "Welcome to our app!");
sendNotification("sms", "Your OTP is 123456");
sendNotification("push", "You have a new message");

Output

๐Ÿ“ง Email sent: Welcome to our app!
๐Ÿ“ฑ SMS sent: Your OTP is 123456
๐Ÿ”” Push notification sent: You have a new message

Pro Tip: Scalable Version (Map Instead of Switch)

When types grow, use a map-based approach ๐Ÿ‘‡

const strategies = {
  email: () => new EmailNotification(),
  sms: () => new SMSNotification(),
  push: () => new PushNotification(),
};
 
class NotificationFactory {
  static create(type) {
    const creator = strategies[type];
    if (!creator) throw new Error("Invalid notification type");
    return creator();
  }
}

๐Ÿ”ฅ This is how it's done in production-grade Node.js apps.

Why This Is a Good Factory Pattern

You gain several superpowers:

โœ… Single Responsibility: Each notification class handles its own sending logic โœ… Open/Closed Principle: Add WhatsApp notifications later without touching business logic โœ… Easy to Test & Mock: Each notification type can be tested independently โœ… Clean Separation of Concerns: Business logic doesn't know about implementation details

Why This Wins

  1. Clean Code: Your business logic doesn't need to know how emails, SMS, or push notifications work. It just calls sendNotification().

  2. Scalability: Want to add WhatsApp notifications next month? Just add a new class and update the factory. You don't touch the rest of your codebase.

  3. Maintainability: If the email sending logic changes, you only update the EmailNotification class. The factory and business logic remain unchanged.

Real-World Benefits

Before Factory Pattern

// Business logic - messy!
function notifyUser(type, message) {
    if (type === "email") {
        // Email sending logic
        console.log(`๐Ÿ“ง Email sent: ${message}`);
    } else if (type === "sms") {
        // SMS sending logic
        console.log(`๐Ÿ“ฑ SMS sent: ${message}`);
    } else if (type === "push") {
        // Push notification logic
        console.log(`๐Ÿ”” Push notification sent: ${message}`);
    }
    // This gets messy fast when you add more types...
}

After Factory Pattern

// Business logic - clean!
const NotificationFactory = require("./NotificationFactory");
 
function notifyUser(type, message) {
    const notification = NotificationFactory.create(type);
    notification.send(message);
    // Clean, simple, extensible!
}

A Quick Questionnaire for You

Before you go, ask yourself these three questions about your current project:

  1. The "If" Test: Search your codebase for "if". Are you checking if (type === 'email') or if (type === 'sms') inside your business logic? If yes, you need a Factory.

  2. The Extension Test: If you need to add a new notification type (like WhatsApp or Slack), do you have to modify multiple files? With a Factory, you only add a new class and update the factory map.

  3. The Testability Test: Can you easily mock different notification types in your unit tests? A Factory makes it trivial to swap implementations for testing.

Best Practices

When implementing the Factory Pattern in Node.js, keep these tips in mind:

  1. Keep it Pure: Factory methods should be pure functionsโ€”same input, same output. Avoid side effects.

  2. Handle Edge Cases: Always provide a default case for unknown types. Don't let your factory crash on unexpected data.

  3. Use TypeScript: If you're using TypeScript, define interfaces for your standard contract. This gives you compile-time safety.

  4. Test Each Implementation: Write unit tests for each concrete class (EmailNotification, SMSNotification, etc.). This ensures each notification type works correctly.

  5. Document the Interface: Make sure your team knows what methods each notification class must implement. Consider creating a TypeScript interface or base class that enforces the contract.

Conclusion

The Factory Pattern is your secret weapon for writing clean, maintainable code. It eliminates scattered if/else statements and creates a single point of control for object creation.

Whether you're handling notifications, processing different payment methods, or managing various data transformations, the Factory Pattern provides a scalable, maintainable solution that will save you hours of debugging and refactoring.

The notification example we covered is just the beginning. You can apply this same pattern to any scenario where you need to create different types of objects based on a type parameter.

Happy coding! Let me know if you implement this pattern in your next Node.js project. ๐Ÿš€