Node.js Design Patterns - Third Edition

Book description

Learn proven patterns, techniques, and tricks to take full advantage of the Node.js platform. Master well-known design principles to create applications that are readable, extensible, and that can grow big.

Key Features

  • Learn how to create solid server-side applications by leveraging the full power of Node.js 14
  • Understand how Node.js works and learn how to take full advantage of its core components as well as the solutions offered by its ecosystem
  • Avoid common mistakes and use proven patterns to create production grade Node.js applications

Book Description

In this book, we will show you how to implement a series of best practices and design patterns to help you create efficient and robust Node.js applications with ease.

We kick off by exploring the basics of Node.js, analyzing its asynchronous event driven architecture and its fundamental design patterns. We then show you how to build asynchronous control flow patterns with callbacks, promises and async/await. Next, we dive into Node.js streams, unveiling their power and showing you how to use them at their full capacity. Following streams is an analysis of different creational, structural, and behavioral design patterns that take full advantage of JavaScript and Node.js. Lastly, the book dives into more advanced concepts such as Universal JavaScript, scalability and messaging patterns to help you build enterprise-grade distributed applications.

Throughout the book, you'll see Node.js in action with the help of several real-life examples leveraging technologies such as LevelDB, Redis, RabbitMQ, ZeroMQ, and many others. They will be used to demonstrate a pattern or technique, but they will also give you a great introduction to the Node.js ecosystem and its set of solutions.

What you will learn

  • Become comfortable with writing asynchronous code by leveraging callbacks, promises, and the async/await syntax
  • Leverage Node.js streams to create data-driven asynchronous processing pipelines
  • Implement well-known software design patterns to create production grade applications
  • Share code between Node.js and the browser and take advantage of full-stack JavaScript
  • Build and scale microservices and distributed systems powered by Node.js
  • Use Node.js in conjunction with other powerful technologies such as Redis, RabbitMQ, ZeroMQ, and LevelDB

Who this book is for

This book is for developers and software architects who have some prior basic knowledge of JavaScript and Node.js and now want to get the most out of these technologies in terms of productivity, design quality, and scalability. Software professionals with intermediate experience in Node.js and JavaScript will also find valuable the more advanced patterns and techniques presented in this book.

This book assumes that you have an intermediate understanding of web application development, databases, and software design principles.

Publisher resources

Download Example Code

Table of contents

  1. Preface
    1. What you need for this book
    2. Who this book is for
    3. What this book covers
    4. To get the most out of this book
    5. Get in touch
  2. The Node.js Platform
    1. The Node.js philosophy
      1. Small core
      2. Small modules
      3. Small surface area
      4. Simplicity and pragmatism
    2. How Node.js works
      1. I/O is slow
      2. Blocking I/O
      3. Non-blocking I/O
      4. Event demultiplexing
      5. The reactor pattern
      6. Libuv, the I/O engine of Node.js
      7. The recipe for Node.js
    3. JavaScript in Node.js
      1. Run the latest JavaScript with confidence
      2. The module system
      3. Full access to operating system services
      4. Running native code
    4. Summary
  3. The Module System
    1. The need for modules
    2. Module systems in JavaScript and Node.js
    3. The module system and its patterns
      1. The revealing module pattern
    4. CommonJS modules
      1. A homemade module loader
      2. Defining a module
      3. module.exports versus exports
      4. The require function is synchronous
      5. The resolving algorithm
      6. The module cache
      7. Circular dependencies
    5. Module definition patterns
      1. Named exports
      2. Exporting a function
      3. Exporting a class
      4. Exporting an instance
      5. Modifying other modules or the global scope
    6. ESM: ECMAScript modules
      1. Using ESM in Node.js
      2. Named exports and imports
      3. Default exports and imports
      4. Mixed exports
      5. Module identifiers
      6. Async imports
      7. Module loading in depth
        1. Loading phases
        2. Read-only live bindings
        3. Circular dependency resolution
      8. Modifying other modules
    7. ESM and CommonJS differences and interoperability
      1. ESM runs in strict mode
      2. Missing references in ESM
      3. Interoperability
    8. Summary
  4. Callbacks and Events
    1. The Callback pattern
      1. The continuation-passing style
        1. Synchronous CPS
        2. Asynchronous CPS
        3. Non-CPS callbacks
      2. Synchronous or asynchronous?
        1. An unpredictable function
        2. Unleashing Zalgo
        3. Using synchronous APIs
        4. Guaranteeing asynchronicity with deferred execution
      3. Node.js callback conventions
        1. The callback comes last
        2. Any error always comes first
        3. Propagating errors
        4. Uncaught exceptions
    2. The Observer pattern
      1. The EventEmitter
      2. Creating and using the EventEmitter
      3. Propagating errors
      4. Making any object observable
      5. EventEmitter and memory leaks
      6. Synchronous and asynchronous events
      7. EventEmitter versus callbacks
      8. Combining callbacks and events
    3. Summary
    4. Exercises
  5. Asynchronous Control Flow Patterns with Callbacks
    1. The difficulties of asynchronous programming
      1. Creating a simple web spider
      2. Callback hell
    2. Callback best practices and control flow patterns
      1. Callback discipline
      2. Applying the callback discipline
      3. Sequential execution
        1. Executing a known set of tasks in sequence
        2. Sequential iteration
      4. Parallel execution
        1. Web spider version 3
        2. The pattern
        3. Fixing race conditions with concurrent tasks
      5. Limited parallel execution
        1. Limiting concurrency
        2. Globally limiting concurrency
    3. The async library
    4. Summary
    5. Exercises
  6. Asynchronous Control Flow Patterns with Promises and Async/Await
    1. Promises
      1. What is a promise?
      2. Promises/A+ and thenables
      3. The promise API
      4. Creating a promise
      5. Promisification
      6. Sequential execution and iteration
      7. Parallel execution
      8. Limited parallel execution
        1. Implementing the TaskQueue class with promises
        2. Updating the web spider
    2. Async/await
      1. Async functions and the await expression
      2. Error handling with async/await
        1. A unified try...catch experience
        2. The "return" versus "return await" trap
      3. Sequential execution and iteration
        1. Antipattern – using async/await with Array.forEach for serial execution
      4. Parallel execution
      5. Limited parallel execution
    3. The problem with infinite recursive promise resolution chains
    4. Summary
    5. Exercises
  7. Coding with Streams
    1. Discovering the importance of streams
      1. Buffering versus streaming
      2. Spatial efficiency
        1. Gzipping using a buffered API
        2. Gzipping using streams
      3. Time efficiency
      4. Composability
        1. Adding client-side encryption
        2. Adding server-side decryption
    2. Getting started with streams
      1. Anatomy of streams
      2. Readable streams
        1. Reading from a stream
        2. Implementing Readable streams
      3. Writable streams
        1. Writing to a stream
        2. Backpressure
        3. Implementing Writable streams
      4. Duplex streams
      5. Transform streams
        1. Implementing Transform streams
        2. Filtering and aggregating data with Transform streams
      6. PassThrough streams
        1. Observability
        2. Late piping
      7. Lazy streams
      8. Connecting streams using pipes
        1. Pipes and error handling
        2. Better error handling with pipeline()
    3. Asynchronous control flow patterns with streams
      1. Sequential execution
      2. Unordered parallel execution
        1. Implementing an unordered parallel stream
        2. Implementing a URL status monitoring application
      3. Unordered limited parallel execution
      4. Ordered parallel execution
    4. Piping patterns
      1. Combining streams
        1. Implementing a combined stream
      2. Forking streams
        1. Implementing a multiple checksum generator
      3. Merging streams
        1. Merging text files
      4. Multiplexing and demultiplexing
        1. Building a remote logger
        2. Multiplexing and demultiplexing object streams
    5. Summary
    6. Exercises
  8. Creational Design Patterns
    1. Factory
      1. Decoupling object creation and implementation
      2. A mechanism to enforce encapsulation
      3. Building a simple code profiler
      4. In the wild
    2. Builder
      1. Implementing a URL object builder
      2. In the wild
    3. Revealing Constructor
      1. Building an immutable buffer
      2. In the wild
    4. Singleton
    5. Wiring modules
      1. Singleton dependencies
      2. Dependency Injection
    6. Summary
    7. Exercises
  9. Structural Design Patterns
    1. Proxy
      1. Techniques for implementing proxies
        1. Object composition
        2. Object augmentation
        3. The built-in Proxy object
        4. A comparison of the different proxying techniques
      2. Creating a logging Writable stream
      3. Change observer with Proxy
      4. In the wild
    2. Decorator
      1. Techniques for implementing decorators
        1. Composition
        2. Object augmentation
        3. Decorating with the Proxy object
      2. Decorating a LevelUP database
        1. Introducing LevelUP and LevelDB
        2. Implementing a LevelUP plugin
      3. In the wild
    3. The line between proxy and decorator
    4. Adapter
      1. Using LevelUP through the filesystem API
      2. In the wild
    5. Summary
    6. Exercises
  10. Behavioral Design Patterns
    1. Strategy
      1. Multi-format configuration objects
      2. In the wild
    2. State
      1. Implementing a basic failsafe socket
    3. Template
      1. A configuration manager template
      2. In the wild
    4. Iterator
      1. The iterator protocol
      2. The iterable protocol
      3. Iterators and iterables as a native JavaScript interface
      4. Generators
        1. Generators in theory
        2. A simple generator function
        3. Controlling a generator iterator
        4. How to use generators in place of iterators
      5. Async iterators
      6. Async generators
      7. Async iterators and Node.js streams
      8. In the wild
    5. Middleware
      1. Middleware in Express
      2. Middleware as a pattern
      3. Creating a middleware framework for ZeroMQ
        1. The Middleware Manager
        2. Implementing the middleware to process messages
        3. Using the ZeroMQ middleware framework
      4. In the wild
    6. Command
      1. The Task pattern
      2. A more complex command
    7. Summary
    8. Exercises
  11. Universal JavaScript for Web Applications
    1. Sharing code with the browser
      1. JavaScript modules in a cross-platform context
        1. Module bundlers
        2. How a module bundler works
        3. Using webpack
    2. Fundamentals of cross-platform development
      1. Runtime code branching
        1. Challenges of runtime code branching
      2. Build-time code branching
      3. Module swapping
      4. Design patterns for cross-platform development
    3. A brief introduction to React
      1. Hello React
      2. Alternatives to react.createElement
      3. Stateful components
    4. Creating a Universal JavaScript app
      1. Frontend-only app
      2. Server-side rendering
      3. Asynchronous data retrieval
      4. Universal data retrieval
        1. Two-pass rendering
        2. Async pages
        3. Implementing async pages
    5. Summary
    6. Exercises
  12. Advanced Recipes
    1. Dealing with asynchronously initialized components
      1. The issue with asynchronously initialized components
        1. Local initialization check
        2. Delayed startup
      2. Pre-initialization queues
      3. In the wild
    2. Asynchronous request batching and caching
      1. What's asynchronous request batching?
      2. Optimal asynchronous request caching
      3. An API server without caching or batching
      4. Batching and caching with promises
        1. Batching requests in the total sales web server
        2. Caching requests in the total sales web server
        3. Notes about implementing caching mechanisms
    3. Canceling asynchronous operations
      1. A basic recipe for creating cancelable functions
      2. Wrapping asynchronous invocations
      3. Cancelable async functions with generators
    4. Running CPU-bound tasks
      1. Solving the subset sum problem
      2. Interleaving with setImmediate
        1. Interleaving the steps of the subset sum algorithm
        2. Considerations on the interleaving approach
      3. Using external processes
        1. Delegating the subset sum task to an external process
        2. Considerations for the multi-process approach
      4. Using worker threads
        1. Running the subset sum task in a worker thread
      5. Running CPU-bound tasks in production
    5. Summary
    6. Exercises
  13. Scalability and Architectural Patterns
    1. An introduction to application scaling
      1. Scaling Node.js applications
      2. The three dimensions of scalability
    2. Cloning and load balancing
      1. The cluster module
        1. Notes on the behavior of the cluster module
        2. Building a simple HTTP server
        3. Scaling with the cluster module
        4. Resiliency and availability with the cluster module
        5. Zero-downtime restart
      2. Dealing with stateful communications
        1. Sharing the state across multiple instances
        2. Sticky load balancing
      3. Scaling with a reverse proxy
        1. Load balancing with Nginx
      4. Dynamic horizontal scaling
        1. Using a service registry
        2. Implementing a dynamic load balancer with http-proxy and Consul
      5. Peer-to-peer load balancing
        1. Implementing an HTTP client that can balance requests across multiple servers
      6. Scaling applications using containers
        1. What is a container?
        2. Creating and running a container with Docker
        3. What is Kubernetes?
        4. Deploying and scaling an application on Kubernetes
    3. Decomposing complex applications
      1. Monolithic architecture
      2. The microservice architecture
        1. An example of a microservice architecture
        2. Microservices – advantages and disadvantages
      3. Integration patterns in a microservice architecture
        1. The API proxy
        2. API orchestration
        3. Integration with a message broker
    4. Summary
    5. Exercises
  14. Messaging and Integration Patterns
    1. Fundamentals of a messaging system
      1. One way versus request/reply patterns
      2. Message types
        1. Command Messages
        2. Event Messages
        3. Document Messages
      3. Asynchronous messaging, queues, and streams
      4. Peer-to-peer or broker-based messaging
    2. Publish/Subscribe pattern
      1. Building a minimalist real-time chat application
        1. Implementing the server side
        2. Implementing the client side
        3. Running and scaling the chat application
      2. Using Redis as a simple message broker
      3. Peer-to-peer Publish/Subscribe with ZeroMQ
        1. Introducing ZeroMQ
        2. Designing a peer-to-peer architecture for the chat server
        3. Using the ZeroMQ PUB/SUB sockets
      4. Reliable message delivery with queues
        1. Introducing AMQP
        2. Durable subscribers with AMQP and RabbitMQ
      5. Reliable messaging with streams
        1. Characteristics of a streaming platform
        2. Streams versus message queues
        3. Implementing the chat application using Redis Streams
    3. Task distribution patterns
      1. The ZeroMQ Fanout/Fanin pattern
        1. PUSH/PULL sockets
        2. Building a distributed hashsum cracker with ZeroMQ
      2. Pipelines and competing consumers in AMQP
        1. Point-to-point communications and competing consumers
        2. Implementing the hashsum cracker using AMQP
      3. Distributing tasks with Redis Streams
        1. Redis consumer groups
        2. Implementing the hashsum cracker using Redis Streams
    4. Request/Reply patterns
      1. Correlation Identifier
        1. Implementing a request/reply abstraction using correlation identifiers
      2. Return address
        1. Implementing the Return Address pattern in AMQP
    5. Summary
    6. Exercises
  15. Other Books You May Enjoy
  16. Index

Product information

  • Title: Node.js Design Patterns - Third Edition
  • Author(s): Mario Casciaro, Luciano Mammino
  • Release date: July 2020
  • Publisher(s): Packt Publishing
  • ISBN: 9781839214110