More in Backend Dev — page 40

Top-Level Await: `await` Without an `async` Function
Top-level await lets you use `await` directly in an ES module, no `async` function needed. Use it to initialize resources like database connections on startup. The footgun: the entire module's execution blocks until the promise resolves, delaying startup.

Promise.any(): Get the Fastest Successful Result
Promise.any() is a race where only finishers count. It returns the value of the first promise to succeed, ignoring any that fail. Use it to query redundant endpoints and take the first successful response.

Promise.allSettled(): Never Fail a Batch of Promises
Promise.allSettled() waits for every promise in a set to finish, success or fail, without short-circuiting. Use it for independent tasks, like multiple API calls, where you need the outcome of each.

Promise.race(): First Promise to Settle Wins
Promise.race() returns a promise that mirrors the outcome of the first promise in a set to finish—the winner takes all, whether it resolves or rejects. Use it to set a timeout on a network request.

Promise.all(): Wait for Multiple Promises at Once
Promise.all() runs multiple promises in parallel, resolving only when all have succeeded. It's for when you need data from several API endpoints to render a single component.

Async/Await: Write Non-Blocking Code That Reads Synchronously
async/await lets you write non-blocking code that reads like simple, synchronous logic. It's used for network requests or database queries without freezing your app. The biggest footgun is using `await` inside a function you forgot to declare as `async`.
Node.js util.promisify: From Callbacks to Promises
util.promisify converts callback-based functions into Promise-based ones, letting you use async/await with older Node.js APIs. It's a bridge for legacy code following the standard (err, value) callback pattern. The footgun: it fails on non-standard signatures.

Promise .catch(): Handling Rejections
`.catch()` is the `try...catch` for promises, intercepting errors (rejections) in a chain. Use it at the end of a promise chain to handle failures from preceding steps, like a failed API call. The footgun: placing it mid-chain can swallow errors.

Promise.then(): Each Call Returns a New Promise
Each `.then()` call returns a new promise, letting you chain async tasks sequentially. This is key for multi-step operations like API calls. The footgun is attaching multiple `.then()`s to the original promise, which executes them in parallel, not in sequence.

JavaScript Promises: Handling Future Values
A Promise is an IOU for a future value from an async operation. Instead of blocking your code, you get an object that will eventually contain the result or an error. They're essential for API calls or file reads.

NPM Scopes: Namespacing Packages to Avoid Collisions
NPM scopes act like a personal folder for your packages, using the `@scope/package` format to avoid name collisions. They are essential for publishing private packages for your team or grouping related public ones.
Node.js Circular Dependencies: The Unfinished Export
When module A requires B, and B requires A, Node.js avoids an infinite loop by returning an unfinished version of one module's exports. This happens in complex apps with tightly coupled modules. The code doesn't crash; it fails later with a TypeError.

Structuring Express Apps with Layered Architecture
Think of your Express app as a three-story building: a web layer for HTTP traffic, a service layer for business logic, and a data layer for your database. This keeps code organized and testable.
Environment Variables: Config Outside Your Code
Think of a .env file as a Post-it note of secrets for your app, kept separate from your codebase. Use it for API keys or database URLs that change between environments. The biggest mistake is committing your .env file to Git, exposing all your secrets.
ES Modules in Node.js: The Modern `import` System
ES Modules bring the browser's `import`/`export` syntax to Node.js, replacing the classic `require()`. Use ESM for modern projects to get features like top-level await. The main footgun is that you can't use `require()`, `__dirname`, or `__filename`.

npm Scripts: Your Project's Command-Line Shortcuts
Think of npm scripts as aliases for your project's common command-line tasks, stored in `package.json`. Use them to run build tools, start servers, or execute tests. The main footgun is forgetting `npm run` for custom scripts like `lint` or `build`.

Dependencies vs. DevDependencies: What's the Difference?
Dependencies are packages your app needs to run in production (like a web framework). DevDependencies are for your development workflow (like a test runner). The footgun is mixing them up, which bloats your production build with unnecessary code.

package.json: The Blueprint for Your Node.js Project
The package.json file is the blueprint for a Node.js project, listing its dependencies and runnable scripts. It's essential for installing libraries (`npm install`) and running tasks (`npm test`).
CommonJS: Node.js's Original Module System
CommonJS treats each file as a private box of code. You share tools using `exports` and import them with `require`. It's the original module system for Node.js, used to organize code into reusable pieces.
Node.js Cluster: Scaling on a Single Machine
The `cluster` module turns a single-threaded Node.js app into a multi-process server that uses all CPU cores. It's ideal for scaling network applications on one machine by sharing a single port.