<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hugo Di Francesco</title>
    <description>The latest articles on DEV Community by Hugo Di Francesco (@hugo__df).</description>
    <link>https://dev.to/hugo__df</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1586%2F1WWnPFQq.jpeg</url>
      <title>DEV Community: Hugo Di Francesco</title>
      <link>https://dev.to/hugo__df</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hugo__df"/>
    <language>en</language>
    <item>
      <title>Celebrating the publication of  "Professional JavaScript" with the Dev.to family</title>
      <dc:creator>Hugo Di Francesco</dc:creator>
      <pubDate>Sun, 20 Oct 2019 12:20:47 +0000</pubDate>
      <link>https://dev.to/hugo__df/celebrating-the-publication-of-professional-javascript-with-the-dev-to-family-4f2b</link>
      <guid>https://dev.to/hugo__df/celebrating-the-publication-of-professional-javascript-with-the-dev-to-family-4f2b</guid>
      <description>&lt;p&gt;"Professional JavaScript" (which I've a co-authored), has recently been published with Packt, it's currently the #1 new release in the "Internet Web Browsers" category on Amazon: &lt;a href="https://www.amazon.com/dp/1838820213"&gt;https://www.amazon.com/dp/1838820213&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a first-time author I was very grateful to work with the editorial team and the other authors. Publishing a book was a longer-term goal for me, which I've now achieved ahead of schedule and the experience has expanded my horizons as a writer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Edit 2&lt;/strong&gt; I've sent through a list of people for the review referral scheme&lt;br&gt;
If anyone is interested in a free ebook copy through the Packt review referral scheme, fill out the form:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edit 1&lt;/strong&gt;: I may reach the referral limit soon, if you're interested, I suggest still filling out the form anyhow and I'll see with my editor 😊&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Thanks again to the amazing team at Packt and everyone who has worked on this (Vinicius Isola, Siyuan Gao, Philip Kirkbride).&lt;/p&gt;

&lt;p&gt;To anyone who has ever read, liked, shared any of my posts anywhere on the internet, thanks! I wouldn't have got here without your support and readership.&lt;/p&gt;

</description>
      <category>writing</category>
      <category>books</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>Yarn Workspaces: monorepo management without Lerna for applications and coding examples</title>
      <dc:creator>Hugo Di Francesco</dc:creator>
      <pubDate>Mon, 05 Aug 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/hugo__df/yarn-workspaces-monorepo-management-without-lerna-for-applications-and-coding-examples-3920</link>
      <guid>https://dev.to/hugo__df/yarn-workspaces-monorepo-management-without-lerna-for-applications-and-coding-examples-3920</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;How to use Yarn Workspaces to manage applications/coding examples.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Lerna isn’t required when you don’t need the git diffing and versioning facilites.&lt;/p&gt;

&lt;p&gt;What’s more Yarn Workspaces are a great lightweight tool to get up and running faster for simple Node.js monorepo actions.&lt;/p&gt;

&lt;p&gt;Example monorepo at: &lt;a href="https://github.com/HugoDF/yarn-workspaces-simple-monorepo"&gt;github.com/HugoDF/yarn-workspaces-simple-monorepo&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Yarn Workspaces vs Lerna
&lt;/h2&gt;

&lt;p&gt;Pros of using workspaces: Yarn Workspaces are part of the standard Yarn toolchain (not downloading an extra dependency). It’s very limited in scope, and de-dupes your installs (ie. makes them faster). This is perfect for managing code examples or a monorepo of applications.&lt;/p&gt;

&lt;p&gt;Cons of workspaces: If you’re dealing with a monorepo of &lt;em&gt;packages&lt;/em&gt; (npm modules, libraries), Lerna provides tooling around publishing/testing only changed files.&lt;/p&gt;

&lt;p&gt;Note: Lerna and Yarn Workspaces are &lt;em&gt;in fact&lt;/em&gt; designed to work together, just use &lt;code&gt;"npmClient": "yarn"&lt;/code&gt; in your &lt;code&gt;lerna.json&lt;/code&gt;. See the &lt;a href="https://yarnpkg.com/lang/en/docs/workspaces/#toc-how-does-it-compare-to-lerna"&gt;Yarn Workspaces Documentation for a short word about the comparison to Lerna&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up Yarn Workspaces
&lt;/h2&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
"private": true,
"workspaces": ["examples/*", "other-example"]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The directory structure looks like the following:&lt;/p&gt;

&lt;p&gt;graph LR;r[root];r--&amp;gt;example;example--&amp;gt;example-1;example--&amp;gt;example-2;r--&amp;gt;other-example;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; : each of the workspaces (packages) need to have a package.json with a unique &lt;code&gt;name&lt;/code&gt; and a valid &lt;code&gt;version&lt;/code&gt;. The root package.json doesn’t need to, it just needs to have &lt;code&gt;"private": true&lt;/code&gt; and &lt;code&gt;"workspaces": []&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bootstrapping the monorepo
&lt;/h2&gt;

&lt;p&gt;Equivalent with Lerna would include a &lt;code&gt;lerna bootstrap&lt;/code&gt;, which run &lt;code&gt;npm install&lt;/code&gt; in all the packages.&lt;/p&gt;

&lt;p&gt;With workspaces since the dependencies are locked from root, you just need to do a &lt;code&gt;yarn&lt;/code&gt; at the top-level.&lt;/p&gt;

&lt;p&gt;For workspaces to work, your “workspace” folders need to have a package.json that contain a &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;version&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Managing your monorepo with &lt;code&gt;yarn workspace&lt;/code&gt; and &lt;code&gt;yarn workspaces&lt;/code&gt; commands
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Run commands in a single package
&lt;/h3&gt;

&lt;p&gt;To run commands in a single package in your monorepo, use the following syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn workspace &amp;lt;package-name&amp;gt; &amp;lt;yarn-command&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ yarn workspace example-1 run test
yarn workspace v1.x.x
yarn run v1.x.x
$ node test.js
test from example 1
✨ Done in 0.23s.
✨ Done in 0.86s.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ yarn workspace example-2 remove lodash.omit
yarn workspace v1.x.x
yarn remove v1.x.x
[1/2] 🗑 Removing module lodash.omit...
[2/2] 🔨 Regenerating lockfile and installing missing dependencies...
success Uninstalled packages.
✨ Done in 2.83s.
✨ Done in 3.58s.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;“package-name” should be the value of found in the package.json under the &lt;code&gt;name&lt;/code&gt; key.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run commands in all packages
&lt;/h3&gt;

&lt;p&gt;To run commands in every package in your monorepo, use the following syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn workspaces &amp;lt;yarn-command&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ yarn workspaces run test
yarn workspaces v1.x.x
yarn run v1.x.x
$ node test.js
test from example 1
✨ Done in 0.22s.
yarn run v1.x.x
$ node test.js
{ public: 'data' } 'Should not display "secret"'
✨ Done in 0.23s.
yarn run v1.x.x
$ echo "Other Example"
Other Example
✨ Done in 0.11s.
✨ Done in 2.15s.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ yarn workspaces run add lodash.omit@latest
yarn workspaces v1.x.x
yarn add v1.x.x
[1/4] 🔍 Resolving packages...
[2/4] 🚚 Fetching packages...
[3/4] 🔗 Linking dependencies...
[4/4] 🔨 Building fresh packages...
success Saved 1 new dependency.
info Direct dependencies
info All dependencies
└─ lodash.omit@4.5.0
✨ Done in 3.31s.
yarn add v1.x.x
[1/4] 🔍 Resolving packages...
[2/4] 🚚 Fetching packages...
[3/4] 🔗 Linking dependencies...
[4/4] 🔨 Building fresh packages...
success Saved 1 new dependency.
info Direct dependencies
info All dependencies
└─ lodash.omit@4.5.0
✨ Done in 2.76s.
yarn add v1.x.x
[1/4] 🔍 Resolving packages...
[2/4] 🚚 Fetching packages...
[3/4] 🔗 Linking dependencies...
[4/4] 🔨 Building fresh packages...
success Saved 1 new dependency.
info Direct dependencies
info All dependencies
└─ lodash.omit@4.5.0
✨ Done in 2.63s.
✨ Done in 10.82s.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Example monorepo at: &lt;a href="https://github.com/HugoDF/yarn-workspaces-simple-monorepo"&gt;github.com/HugoDF/yarn-workspaces-simple-monorepo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://unsplash.com/@martinadams?utm_medium=referral&amp;amp;utm_campaign=photographer-credit&amp;amp;utm_content=creditBadge"&gt;unsplash-logo&lt;br&gt;
Martin Adams&lt;/a&gt;&lt;/p&gt;

</description>
      <category>yarn</category>
      <category>monorepo</category>
      <category>node</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Jest Full and Partial Mock/Spy of CommonJS and ES6 Module Imports</title>
      <dc:creator>Hugo Di Francesco</dc:creator>
      <pubDate>Mon, 29 Jul 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/hugo__df/jest-full-and-partial-mock-spy-of-commonjs-and-es6-module-imports-53gf</link>
      <guid>https://dev.to/hugo__df/jest-full-and-partial-mock-spy-of-commonjs-and-es6-module-imports-53gf</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;JavaScript import/require module testing do’s and don’ts with Jest&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The example repository is available at &lt;a href="https://github.com/HugoDF/mock-spy-module-import"&gt;github.com/HugoDF/mock-spy-module-import&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post goes through how to achieve different types of module mocking scenarios with Jest.&lt;/p&gt;

&lt;p&gt;From simple Import interception, to how to approach stubbing out an internal function call or Mocking only part of a module (by spying…).&lt;/p&gt;

&lt;p&gt;These are methods that work in more specific cases than what the Jest official documentation shows.&lt;/p&gt;

&lt;p&gt;This isn’t strictly a Jest testing guide, the same principles can be applied to any application/tests that need to mock CommonJS or ES Modules.&lt;/p&gt;

&lt;h2&gt;
  
  
  For Context: types of imports
&lt;/h2&gt;

&lt;p&gt;Modern JavaScript has 2 types of imports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CommonJS: Node.js’ built-in import system which uses calls to a global &lt;code&gt;require('module-y')&lt;/code&gt; function, packages on npm expose a CommonJS compatible entry file.&lt;/li&gt;
&lt;li&gt;ES Modules (ESM): modules as defined by the ECMAScript standard. It uses &lt;code&gt;import x from 'module-y'&lt;/code&gt; syntax.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are also (legacy) module loaders like RequireJS and AMD but CommonJS and ESM are the current and future most widespread module definition formats for JavaScript.&lt;/p&gt;

&lt;p&gt;ES Modules have 2 types of exports: named exports and default exports.&lt;/p&gt;

&lt;p&gt;A named export looks likes this: &lt;code&gt;export function myFunc() {}&lt;/code&gt; or &lt;code&gt;export const a = 1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A default export looks like this: &lt;code&gt;export default somethingAlreadyDefined&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A named export can be imported by itself using syntax that looks (and works) a bit like object destructuring: &lt;code&gt;import { myFunc, a } from './some-module'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It can also be imported as a namespace: &lt;code&gt;import * as moduleY from './module-y'&lt;/code&gt; (can now use &lt;code&gt;moduleY.myFunc()&lt;/code&gt; and &lt;code&gt;moduleY.a&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;A default export can only be imported with a default import: &lt;code&gt;import whateverIsDefault from './moduleY'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Theses 2 types of imports can also be mixed and matched, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import"&gt;see &lt;code&gt;import&lt;/code&gt; docs on MDN&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intercepting imports
&lt;/h2&gt;

&lt;p&gt;When unit-testing, you may want to stub/mock out module(s) that have their own battery of unit tests.&lt;/p&gt;

&lt;p&gt;In Jest, this is done with &lt;code&gt;jest.mock('./path/of/module/to/mock', () =&amp;gt; ({ /* fake module */ }))&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In this case the CommonJS and ES6 Module mocks look quite similar. There are a few general gotchas.&lt;/p&gt;

&lt;p&gt;First off, what you’re mocking with (2nd parameter of &lt;code&gt;jest.mock&lt;/code&gt;) is a factory for the module. ie. it’s a function that returns a mock module object.&lt;/p&gt;

&lt;p&gt;Second, if you want to reference a variable from the parent scope of &lt;code&gt;jest.mock&lt;/code&gt; (you want to define your mock module instance for example), you need to prefix the variable name with &lt;code&gt;mock&lt;/code&gt;.For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const mockDb = {
get: jest.fn(),
set: jest.fn()
};
const db = mockDb
// This works
jest.mock('./db', () =&amp;gt; mockDb);
// This doesn't work
jest.mock('./db', () =&amp;gt; db);

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Finally, you should call &lt;code&gt;jest.mock&lt;/code&gt; &lt;em&gt;before&lt;/em&gt; importing the module under test (which itself imports the module we just mocked).&lt;/p&gt;

&lt;p&gt;In practice, Babel ESM -&amp;gt; CommonJS transpilation hoists the &lt;code&gt;jest.mock&lt;/code&gt; call so it’s usually not an issue 🤷‍♀.&lt;/p&gt;

&lt;h3&gt;
  
  
  The CommonJS case
&lt;/h3&gt;

&lt;p&gt;The full test and code under test is at &lt;a href="https://github.com/HugoDF/mock-spy-module-import/tree/master/examples/intercept-imports-cjs"&gt;examples/intercept-imports-cjs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The relevant snippets are the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jest.mock('./db', () =&amp;gt; ({
get: jest.fn(),
set: jest.fn()
}));
const mockDb = require('./db');
const {addTodo, getTodo} = require('./lib');
test('CommonJS &amp;gt; addTodo &amp;gt; inserts with new id', async () =&amp;gt; {
await addTodo({name: 'new todo'});
expect(mockDb.set).toHaveBeenCalledWith('todos:1', {name: 'new todo', id: 1});
});
test('CommonJS &amp;gt; getTodo &amp;gt; returns output of db.get', async () =&amp;gt; {
mockDb.get.mockResolvedValueOnce({
id: 1,
name: 'todo-1'
});
const expected = {
id: 1,
name: 'todo-1'
};
const actual = await getTodo(1);
expect(mockDb.get).toHaveBeenCalledWith('todos:1');
expect(actual).toEqual(expected);
});

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  ES Modules default export
&lt;/h3&gt;

&lt;p&gt;The full test and code under test is at &lt;a href="https://github.com/HugoDF/mock-spy-module-import/tree/master/examples/intercept-imports-esm-default"&gt;examples/intercept-imports-esm-default&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import mockDb from './db';
import lib from './lib';
jest.mock('./db', () =&amp;gt; ({
get: jest.fn(),
set: jest.fn()
}));
const {addTodo, getTodo} = lib;
test('ESM Default Export &amp;gt; addTodo &amp;gt; inserts with new id', async () =&amp;gt; {
await addTodo({name: 'new todo'});
expect(mockDb.set).toHaveBeenCalledWith('todos:1', {name: 'new todo', id: 1});
});
test('ESM Default Export &amp;gt; getTodo &amp;gt; returns output of db.get', async () =&amp;gt; {
mockDb.get.mockResolvedValueOnce({
id: 1,
name: 'todo-1'
});
const expected = {
id: 1,
name: 'todo-1'
};
const actual = await getTodo(1);
expect(mockDb.get).toHaveBeenCalledWith('todos:1');
expect(actual).toEqual(expected);
});

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  ES Modules named export
&lt;/h3&gt;

&lt;p&gt;The full test and code under test is at &lt;a href="https://github.com/HugoDF/mock-spy-module-import/tree/master/examples/intercept-imports-esm-named"&gt;examples/intercept-imports-esm-named&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import * as mockDb from './db';
import {addTodo, getTodo} from './lib';
jest.mock('./db', () =&amp;gt; ({
get: jest.fn(),
set: jest.fn()
}));
test('ESM named export &amp;gt; addTodo &amp;gt; inserts with new id', async () =&amp;gt; {
await addTodo({name: 'new todo'});
expect(mockDb.set).toHaveBeenCalledWith('todos:1', {name: 'new todo', id: 1});
});
test('ESM named export &amp;gt; getTodo &amp;gt; returns output of db.get', async () =&amp;gt; {
mockDb.get.mockResolvedValueOnce({
id: 1,
name: 'todo-1'
});
const expected = {
id: 1,
name: 'todo-1'
};
const actual = await getTodo(1);
expect(mockDb.get).toHaveBeenCalledWith('todos:1');
expect(actual).toEqual(expected);
});

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Spying/Stubbing calls to internal module functions
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Warning: you should not be spying/stubbing module internals, that’s your test reaching into the implementation, which means test and code under test are tightly coupled&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Concept: “calling through” (as opposed to mocking).&lt;/p&gt;

&lt;p&gt;An internal/private/helper function that isn’t exported should be tested through its public interface, ie. not by calling it, since it’s not exported, but by calling the function that calls it.&lt;/p&gt;

&lt;p&gt;Testing its functionality is the responsibility of the tests of the function(s) that consume said helper.&lt;/p&gt;

&lt;p&gt;This is for the cases where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you don’t have the time to extract the function but the complexity is too high to test through (from the function under test into the internal function)

&lt;ul&gt;
&lt;li&gt;solution: you should probably &lt;em&gt;make&lt;/em&gt; time&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;the internal function belongs in said module but its complexity make it unwieldy to test through.

&lt;ul&gt;
&lt;li&gt;solution: you should probably extract it&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;the function is not strictly internal, it’s exported and unit tested, thereforce calling through would duplicate the tests.

&lt;ul&gt;
&lt;li&gt;solution: you should definitely extract it&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the following cases we’ll be looking to stub/mock/spy the internal &lt;code&gt;makeKey&lt;/code&gt; function. This is purely for academic purposes since, we’ve shown in the section above how to &lt;strong&gt;test through&lt;/strong&gt; the &lt;code&gt;getTodo&lt;/code&gt; call.&lt;/p&gt;

&lt;p&gt;In that situation we were testing &lt;code&gt;expect(mockDb.get).toHaveBeenCalledWith('todos:1');&lt;/code&gt; (see &lt;a href="https://github.com/HugoDF/mock-spy-module-import/tree/master/examples/intercept-imports-cjs/lib.jest-test.js"&gt;examples/intercept-imports-cjs/lib.jest-test.js&lt;/a&gt;). The generation of the &lt;code&gt;todos:1&lt;/code&gt; key is the functionality of &lt;code&gt;makeKey&lt;/code&gt;, that’s an example of testing by &lt;strong&gt;calling through&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  CommonJS
&lt;/h3&gt;

&lt;h4&gt;
  
  
  A module where internal functions can’t be mocked
&lt;/h4&gt;

&lt;p&gt;Code listing lifted from &lt;a href="https://github.com/HugoDF/mock-spy-module-import/tree/master/examples/spy-internal-calls-cjs/lib.fail.js"&gt;examples/spy-internal-calls-cjs/lib.fail.js&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const db = require('./db');
const keyPrefix = 'todos';
const makeKey = key =&amp;gt; `${keyPrefix}:${key}`;
function getTodo(id) {
return db.get(makeKey(id));
}
module.exports = {
makeKey,
getTodo
};

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As you can see when you run the &lt;a href="https://github.com/HugoDF/mock-spy-module-import/tree/master/examples/spy-internal-calls-cjs/lib.fail.jest-test.js"&gt;examples/spy-internal-calls-cjs/lib.fail.jest-test.js&lt;/a&gt; tests, there’s no way to intercept calls to &lt;code&gt;makeKey&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  A module where internal function &lt;em&gt;can&lt;/em&gt; be mocked
&lt;/h4&gt;

&lt;p&gt;Code listing lifted from &lt;a href="https://github.com/HugoDF/mock-spy-module-import/tree/master/examples/spy-internal-calls-cjs/lib.js"&gt;examples/spy-internal-calls-cjs/lib.js&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const db = require('./db');
const keyPrefix = 'todos';
const makeKey = key =&amp;gt; `${keyPrefix}:${key}`;
const lib = {
// Could also define makeKey inline like so:
 // makeKey(key) { return `${keyPrefix}:${key}` },
 makeKey,
getTodo(id) {
return db.get(lib.makeKey(id));
}
};
module.exports = lib;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Mocking/Spying the internal function
&lt;/h4&gt;

&lt;p&gt;Code listing lifted from &lt;a href="https://github.com/HugoDF/mock-spy-module-import/tree/master/examples/spy-internal-calls-cjs/lib.jest-test.js"&gt;examples/spy-internal-calls-cjs/lib.jest-test.js&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ignore setup code
test("CommonJS &amp;gt; Mocking destructured makeKey doesn't work", async () =&amp;gt; {
const mockMakeKey = jest.fn(() =&amp;gt; 'mock-key');
makeKey = mockMakeKey;
await getTodo(1);
expect(makeKey).not.toHaveBeenCalled();
expect(mockDb.get).not.toHaveBeenCalledWith('mock-key');
});
test('CommonJS &amp;gt; Mocking lib.makeKey works', async () =&amp;gt; {
const mockMakeKey = jest.fn(() =&amp;gt; 'mock-key');
lib.makeKey = mockMakeKey;
await getTodo(1);
expect(mockMakeKey).toHaveBeenCalledWith(1);
expect(mockDb.get).toHaveBeenCalledWith('mock-key');
});
test('CommonJS &amp;gt; Spying lib.makeKey works', async () =&amp;gt; {
const makeKeySpy = jest
.spyOn(lib, 'makeKey')
.mockImplementationOnce(() =&amp;gt; 'mock-key');
await getTodo(1);
expect(makeKeySpy).toHaveBeenCalled();
expect(mockDb.get).toHaveBeenCalledWith('mock-key');
});

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;From the above we can see that with the setup from the previous section (see &lt;a href="https://github.com/HugoDF/mock-spy-module-import/tree/master/examples/spy-internal-calls-cjs/lib.js"&gt;examples/spy-internal-calls-cjs/lib.js&lt;/a&gt;), we’re able to both replace the implementation of &lt;code&gt;lib.makeKey&lt;/code&gt; with a mock and spy on it.&lt;/p&gt;

&lt;p&gt;We’re still unable to replace our reference to it. That’s because when we destructure &lt;code&gt;lib&lt;/code&gt; to extract &lt;code&gt;makeKey&lt;/code&gt; we create a copy of the reference ie. &lt;code&gt;makeKey = newValue&lt;/code&gt; changes the implementation of the &lt;code&gt;makeKey&lt;/code&gt; variable we have in our test file but doesn’t replace the behaviour of &lt;code&gt;lib.makeKey&lt;/code&gt; (which is what &lt;code&gt;getTodo&lt;/code&gt; is calling).&lt;/p&gt;

&lt;p&gt;To illustrate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const lib = require('./lib');
let {makeKey} = lib;
makeKey = 'something';
// `lib.makeKey` and `makeKey` are now different...

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  ES Modules
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Difficulty of Named exports
&lt;/h4&gt;

&lt;p&gt;In the case of ES6 Modules, semantically, it’s quite difficult to set the code up in a way that would work with named exports, the following code doesn’t quite work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import db from './db';
const keyPrefix = 'todos';
export const makeKey = key =&amp;gt; `${keyPrefix}:${key}`;
export function getTodo(id) {
return db.get(makeKey(id));
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Code listing lifted from &lt;a href="https://github.com/HugoDF/mock-spy-module-import/tree/master/examples/spy-internal-calls-esm/lib.named-export.js"&gt;examples/spy-internal-calls-esm/lib.named-export.js&lt;/a&gt;, tests showing there’s no simple way to mock/spy on &lt;code&gt;makeKey&lt;/code&gt; are at &lt;a href="https://github.com/HugoDF/mock-spy-module-import/tree/master/examples/spy-internal-calls-esm/lib.named-export.jest-test.js"&gt;examples/spy-internal-calls-esm/lib.named-export.jest-test.js&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Unmockeable modules with default exports
&lt;/h4&gt;

&lt;p&gt;Code listing lifted from &lt;a href="https://github.com/HugoDF/mock-spy-module-import/tree/master/examples/spy-internal-calls-esm/lib.default-export.js"&gt;examples/spy-internal-calls-esm/lib.default-export.js&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import db from './db';
const keyPrefix = 'todos';
const makeKey = key =&amp;gt; `${keyPrefix}:${key}`;
function getTodo(id) {
return db.get(makeKey(id));
}
const lib = {
makeKey,
getTodo
};
export default lib;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Tests showing there’s no simple way to mock/spy on &lt;code&gt;makeKey&lt;/code&gt; are at &lt;a href="https://github.com/HugoDF/mock-spy-module-import/tree/master/examples/spy-internal-calls-esm/lib.default-export.jest-test.js"&gt;examples/spy-internal-calls-esm/lib.default-export.jest-test.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The reason this doesn’t work is the same as the CommonJS example, &lt;code&gt;makeKey&lt;/code&gt; is directly referenced and that reference can’t be modified from outside of the module.&lt;/p&gt;

&lt;p&gt;Anything attempting import it would make a copy and therefore wouldn’t modify the internal reference.&lt;/p&gt;

&lt;h4&gt;
  
  
  Mock/Spy setup with ESM
&lt;/h4&gt;

&lt;p&gt;Code listing lifted from &lt;a href="https://github.com/HugoDF/mock-spy-module-import/tree/master/examples/spy-internal-calls-esm/lib.js"&gt;examples/spy-internal-calls-esm/lib.js&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import db from './db';
const keyPrefix = 'todos';
const makeKey = key =&amp;gt; `${keyPrefix}:${key}`;
const lib = {
// Could also define makeKey inline like so:
 // makeKey(key) { return `${keyPrefix}:${key}` },
 makeKey,
getTodo(id) {
return db.get(lib.makeKey(id));
}
};
export default lib;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Passing tests for the above are at &lt;a href="https://github.com/HugoDF/mock-spy-module-import/tree/master/examples/spy-internal-calls-esm/lib.jest-test.js"&gt;examples/spy-internal-calls-esm/lib.jest-test.js&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; , it would be possible to do something similar with named exports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import db from './db';
const keyPrefix = 'todos';
const makeKey = key =&amp;gt; `${keyPrefix}:${key}`;
export const lib = {
// Could also define makeKey inline like so:
 // makeKey(key) { return `${keyPrefix}:${key}` },
 makeKey,
getTodo(id) {
return db.get(lib.makeKey(id));
}
};

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The key point is around exporting a &lt;code&gt;lib&lt;/code&gt; object and referencing that same object when calling &lt;code&gt;makeKey&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mocking internals is the same with ESM/CommonJS
&lt;/h3&gt;

&lt;p&gt;Being able to mock a part of a module is all about references.&lt;/p&gt;

&lt;p&gt;If a function is calling another function using a reference that’s not accessible from outside of the module (more specifically from our the test), then it can’t be mocked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spy on imports or mock part of a module by “referencing the module”
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Warning: this will cause you to change the way you write your code just to accomodate a specific type of testing.&lt;/p&gt;

&lt;p&gt;This will break if anyone decides to get a copy of the module’s function instead of calling &lt;code&gt;module.fn()&lt;/code&gt; directly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  CommonJS: Spy import/mock part of a module with Jest
&lt;/h3&gt;

&lt;p&gt;Code listing lifted from &lt;a href="https://github.com/HugoDF/mock-spy-module-import/tree/master/examples/spy-module-cjs/lib.js"&gt;examples/spy-module-cjs/lib.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Note how the &lt;code&gt;db&lt;/code&gt; module is imported without destructuring and how any calls to it are done using &lt;code&gt;db.method()&lt;/code&gt; calls.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const db = require('./db');
const keyPrefix = 'todos';
const makeKey = key =&amp;gt; `${keyPrefix}:${key}`;
let autoId = 1;
async function addTodo(todo) {
const id = autoId++;
const insertable = {
...todo,
id
};
await db.set(makeKey(id), insertable);
}
function getTodo(id) {
return db.get(makeKey(id));
}
module.exports = {
addTodo,
getTodo
};

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We are now able to spy on &lt;code&gt;db.method&lt;/code&gt; using the following approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const db = require('./db');
const {addTodo, getTodo} = require('./lib');
beforeEach(() =&amp;gt; jest.clearAllMocks());
test('CommonJS &amp;gt; addTodo &amp;gt; inserts with new id', async () =&amp;gt; {
const dbSetSpy = jest.spyOn(db, 'set').mockImplementation(() =&amp;gt; {});
await addTodo({name: 'new todo'});
expect(dbSetSpy).toHaveBeenCalledWith('todos:1', {name: 'new todo', id: 1});
});
test('CommonJS &amp;gt; getTodo &amp;gt; returns output of db.get', async () =&amp;gt; {
const dbGetSpy = jest.spyOn(db, 'get').mockResolvedValueOnce({
id: 1,
name: 'todo-1'
});
const expected = {
id: 1,
name: 'todo-1'
};
const actual = await getTodo(1);
expect(dbGetSpy).toHaveBeenCalledWith('todos:1');
expect(actual).toEqual(expected);
});

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Notice how we’re not calling &lt;code&gt;jest.mock()&lt;/code&gt;. Instead we’re mocking/spying only a specific function of the module when we need to by modifying the &lt;code&gt;db&lt;/code&gt; module implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  ES6 Modules: Spy import/mock part of a module with Jest
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Default exports
&lt;/h4&gt;

&lt;p&gt;Assuming our &lt;code&gt;db.js&lt;/code&gt; module exports in the following manner (see &lt;a href="https://github.com/HugoDF/mock-spy-module-import/tree/master/examples/spy-module-esm-default/db.js"&gt;examples/spy-module-esm-default/db.js&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const data = {};
async function get(k) {
return data[k];
}
async function set(k, v) {
data[k] = v;
}
const db = {
get,
set
};
export default db;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can then import it as follows (code listing lifted from &lt;a href="https://github.com/HugoDF/mock-spy-module-import/tree/master/examples/spy-module-esm-default/lib.js"&gt;examples/spy-module-esm-default/lib.js&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import db from './db';
const keyPrefix = 'todos';
const makeKey = key =&amp;gt; `${keyPrefix}:${key}`;
let autoId = 1;
function addTodo(todo) {
const id = autoId++;
const insertable = {
...todo,
id
};
return db.set(makeKey(id), insertable);
}
function getTodo(id) {
return db.get(makeKey(id));
}
const lib = {
addTodo,
getTodo
};
export default lib;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Spying on the import/mocking part of the module becomes possible in the following fashion (full code at &lt;a href="https://github.com/HugoDF/mock-spy-module-import/tree/master/examples/spy-module-esm-default/lib.jest-test.js"&gt;examples/spy-module-esm-default/lib.jest-test.js&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import db from './db';
import lib from './lib';
const {addTodo, getTodo} = lib;
beforeEach(() =&amp;gt; jest.clearAllMocks());
test('ESM Default Export &amp;gt; addTodo &amp;gt; inserts with new id', async () =&amp;gt; {
const dbSetSpy = jest.spyOn(db, 'set').mockImplementationOnce(() =&amp;gt; {});
await addTodo({name: 'new todo'});
expect(dbSetSpy).toHaveBeenCalledWith('todos:1', {name: 'new todo', id: 1});
});
test('ESM Default Export &amp;gt; getTodo &amp;gt; returns output of db.get', async () =&amp;gt; {
const dbGetSpy = jest.spyOn(db, 'get').mockResolvedValueOnce({
id: 1,
name: 'todo-1'
});
const expected = {
id: 1,
name: 'todo-1'
};
const actual = await getTodo(1);
expect(dbGetSpy).toHaveBeenCalledWith('todos:1');
expect(actual).toEqual(expected);
});

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Notice how we don’t mock the &lt;code&gt;db&lt;/code&gt; module with a &lt;code&gt;jest.mock()&lt;/code&gt; call. Again we spy on the method that we’re interested in stubbing/spying for a particular test.&lt;/p&gt;

&lt;p&gt;We leverage &lt;code&gt;mockImplementationOnce()&lt;/code&gt; to avoid calling the real function (which you might not always want to do).&lt;/p&gt;

&lt;h4&gt;
  
  
  Named exports + “import * as alias from ‘module-name’”
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: I’ve not read the full spec, the fact that this works might be a quirk of the Babel ES2015 module transpilation&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Assuming we’ve defined &lt;code&gt;db.js&lt;/code&gt; as follows (using named exports, see the file at &lt;a href="https://github.com/HugoDF/mock-spy-module-import/tree/master/examples/spy-module-esm-named/db.js"&gt;examples/spy-module-esm-named/db.js&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const data = {};
export async function get(k) {
return data[k];
}
export async function set(k, v) {
data[k] = v;
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can import all the named exports under an alias with &lt;code&gt;import * as db from './db'&lt;/code&gt; (code listing lifted from &lt;a href="https://github.com/HugoDF/mock-spy-module-import/tree/master/examples/spy-module-esm-named/lib.js"&gt;examples/spy-module-esm-named/lib.js&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import * as db from './db';
const keyPrefix = 'todos';
const makeKey = key =&amp;gt; `${keyPrefix}:${key}`;
let autoId = 1;
export function addTodo(todo) {
const id = autoId++;
const insertable = {
...todo,
id
};
return db.set(makeKey(id), insertable);
}
export function getTodo(id) {
return db.get(makeKey(id));
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The calls to db.set and db.get can be spied/mocked using the following approach (full code test file at &lt;a href="https://github.com/HugoDF/mock-spy-module-import/tree/master/examples/spy-module-esm-named/lib.jest-test.js"&gt;examples/spy-module-esm-named/lib.jest-test.js&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import * as db from './db';
import {addTodo, getTodo} from './lib';
beforeEach(() =&amp;gt; jest.clearAllMocks());
test('ESM named export &amp;gt; addTodo &amp;gt; inserts with new id', async () =&amp;gt; {
const dbSetSpy = jest.spyOn(db, 'set').mockImplementationOnce(() =&amp;gt; {});
await addTodo({name: 'new todo'});
expect(dbSetSpy).toHaveBeenCalledWith('todos:1', {name: 'new todo', id: 1});
});
test('ESM named export &amp;gt; getTodo &amp;gt; returns output of db.get', async () =&amp;gt; {
const dbGetSpy = jest.spyOn(db, 'get').mockResolvedValueOnce({
id: 1,
name: 'todo-1'
});
const expected = {
id: 1,
name: 'todo-1'
};
const actual = await getTodo(1);
expect(dbGetSpy).toHaveBeenCalledWith('todos:1');
expect(actual).toEqual(expected);
});

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;p&gt;The repository with examples is at &lt;a href="https://github.com/HugoDF/mock-spy-module-import"&gt;github.com/HugoDF/mock-spy-module-import&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;See more &lt;a href="https://codewithhugo.com/tags/testing"&gt;Testing&lt;/a&gt; and &lt;a href="https://codewithhugo.com/tags/jest"&gt;Jest&lt;/a&gt; posts on Code with Hugo.&lt;/p&gt;

&lt;p&gt;You can find more Jest/testing/JavaScript content in the &lt;a href="https://buttondown.email/hugo/archive"&gt;Enteprise Node.js and JavaScript newsletter archives&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://unsplash.com/@chrisbecker?utm_medium=referral&amp;amp;utm_campaign=photographer-credit&amp;amp;utm_content=creditBadge"&gt;unsplash-logo&lt;br&gt;
Chris Becker&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>testing</category>
      <category>jest</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Add Search to a Hugo site with Lunr.js and Node.js</title>
      <dc:creator>Hugo Di Francesco</dc:creator>
      <pubDate>Mon, 01 Jul 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/hugo__df/add-search-to-a-hugo-site-with-lunr-js-and-node-js-3m9h</link>
      <guid>https://dev.to/hugo__df/add-search-to-a-hugo-site-with-lunr-js-and-node-js-3m9h</guid>
      <description>&lt;p&gt;&lt;a href="https://gohugo.io"&gt;Hugo&lt;/a&gt; “The world’s fastest framework for building websites” is a great option for &lt;a href="https://jamstack.org/"&gt;JAMStack&lt;/a&gt; (JavaScript, APIs, prebuild Markup) sites.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://lunrjs.com/"&gt;Lunr.js&lt;/a&gt; is “A bit like Solr, but much smaller and not as bright”, it’s a pure JavaScript implementation of a Solr-like search engine.&lt;/p&gt;

&lt;p&gt;One of the only things it doesn’t provide out of the box is Search. It does give you a few options to integrate at &lt;a href="https://gohugo.io/tools/search/"&gt;“Search for your Hugo Website”&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;None were plug and play so I wrote my own simple solution using Lunr.js, a small Node.js script and a few lines of client-side HTML/JavaScript code.&lt;/p&gt;

&lt;p&gt;This is a great example of a benefit of Node.js: it’s a breeze to integrate a pure JavaScript library and pre-compute the search index.&lt;/p&gt;

&lt;p&gt;You can see Search in action at &lt;a href="https://codewithhugo.com/search/?q=lunrjs"&gt;codewithhugo.com/search/?q=lunrjs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Find the full gist at &lt;a href="https://gist.github.com/HugoDF/aac2e529f79cf90d2050d7183571684b"&gt;gist.github.com/HugoDF&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Load up all markdown content with frontmatter
&lt;/h2&gt;

&lt;p&gt;We only want to index a single directory, the “content/posts” directory, our &lt;code&gt;loadPostsWithFrontMatter&lt;/code&gt; function will accept the full path of the posts directory.&lt;/p&gt;

&lt;p&gt;First thing this function does it read the directory contents to get all the file names.It then reads each file and parses the frontmatter and markdown. It flattens the content and the frontmatter data into a single object. It also truncates the content to 3000 characters to avoid generating a huge (2MB+) index file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const fs = require('fs').promises;
const {promisify} = require('util');
const frontMatterParser = require('parser-front-matter');
const parse = promisify(frontMatterParser.parse.bind(frontMatterParser));
async function loadPostsWithFrontMatter(postsDirectoryPath) {
const postNames = await fs.readdir(postsDirectoryPath);
const posts = await Promise.all(
postNames.map(async fileName =&amp;gt; {
const fileContent = await fs.readFile(
`${postsDirectoryPath}/${fileName}`,
'utf8'
);
const {content, data} = await parse(fileContent);
return {
content: content.slice(0, 3000),
...data
};
})
);
return posts;
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating the Lunr.js index
&lt;/h2&gt;

&lt;p&gt;Given a list of posts, we want to use the title as a reference (more on that later), and index the title, content and tags fields.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const lunrjs = require('lunr');
function makeIndex(posts) {
return lunrjs(function() {
this.ref('title');
this.field('title');
this.field('content');
this.field('tags');
posts.forEach(p =&amp;gt; {
this.add(p);
});
});
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;

&lt;p&gt;The following script needs to have the previously defined JavaScript functions in scope to work, and be at the root of the Hugo project in order to read all the posts into the search index.&lt;/p&gt;

&lt;p&gt;See the full file at &lt;a href="https://gist.github.com/HugoDF/aac2e529f79cf90d2050d7183571684b"&gt;gist.github.com/HugoDF/aac2e529f79cf90d2050d7183571684b&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This function actually just logs the stringified index out. To get it into a file, we could add &lt;code&gt;await fs.writeFile('./path/to/index.json', JSON.stringify(index), 'utf8')&lt;/code&gt; or we can redirect the output of a file (which is a bit more flexible).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function run() {
const posts = await loadPostsWithFrontMatter(`${__dirname}/content/post`);
const index = makeIndex(posts);
console.log(JSON.stringify(index));
}
run()
.then(() =&amp;gt; process.exit(0))
.catch(error =&amp;gt; {
console.error(error.stack);
process.exit(1);
});

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Generating the index file
&lt;/h2&gt;

&lt;p&gt;I personally created a &lt;code&gt;static/gen&lt;/code&gt; folder that includes a &lt;code&gt;.gitkeep&lt;/code&gt; file. Added the empty folder to git and then ignored it, then my Lunr.js search index generation command is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node ./build-lunrjs-index.js &amp;gt; static/gen/search-index.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can also just stuff the search index into the root of your static folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node ./build-lunrjs-index.js &amp;gt; static/search-index.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Or even put it in &lt;code&gt;public&lt;/code&gt; directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node ./build-lunrjs-index.js &amp;gt; public/search-index.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In each of these cases, be weary of trying to redirect output to a directory that doesn’t exist (especially in your continuous integration pipeline).&lt;/p&gt;

&lt;h2&gt;
  
  
  Consuming the Lunr.js index client-side
&lt;/h2&gt;

&lt;p&gt;To consume the Lunr.js index, we just have to load it and call &lt;code&gt;lunr.Index.load&lt;/code&gt;, as illustrated below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fetch('/gen/search-index.json').then(function (res) {
return res.json();
}).then(function (data) {
const index = lunr.Index.load(data);
const matches = index.search(searchString);
});

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;A more fully featured integration might be as follows.&lt;/p&gt;

&lt;p&gt;We want a search box (form) with a submit button and a clear link.When the page loads, we first check what the &lt;code&gt;q&lt;/code&gt; param contains by trying to parse it as a &lt;code&gt;URLSearchParams&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If it’s empty, display an information message.&lt;/p&gt;

&lt;p&gt;If there is a search query, we load up the search index using &lt;code&gt;fetch&lt;/code&gt;, load into into memory using &lt;code&gt;lunr.Index.load&lt;/code&gt; and search against it.What we’ve also done before this point is generate a post title -&amp;gt; search result mapping using Hugo slices and a bit of JavaScript to marshal it.&lt;/p&gt;

&lt;p&gt;Using the title -&amp;gt; result mapping, we display relevant search results.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;form method="get" action=""&amp;gt;
&amp;lt;input id="search" name="q" type="text" /&amp;gt;
&amp;lt;button type="submit" class="button"&amp;gt;Search&amp;lt;/button&amp;gt;
&amp;lt;a href="/search"&amp;gt;Clear&amp;lt;/a&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;div id="#app"&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;script src="https://unpkg.com/lunr/lunr.js"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;!-- Generate a list of posts so we can display them --&amp;gt;
{{ $p := slice }}
{{ range (where .Site.RegularPages "Section" "==" "post") }}
{{ $post := dict "link" .RelPermalink "title" .Title "content" (substr .Plain 0 200) -}}
{{ $p = $p | append $post -}}
{{ end }}
&amp;lt;script&amp;gt;
const posts = JSON.parse(
 {{ $p | jsonify }}
);

const query = new URLSearchParams(window.location.search);
const searchString = query.get('q');
document.querySelector('#search').value = searchString;
const $target = document.querySelector('#app');

// Our index uses title as a reference
const postsByTitle = posts.reduce((acc, curr) =&amp;gt; {
 acc[curr.title] = curr;
 return acc;
}, {});

fetch('/gen/search-index.json').then(function (res) {
 return res.json();
}).then(function (data) {
 const index = lunr.Index.load(data);
 const matches = index.search(searchString);
 const matchPosts = [];
 matches.forEach((m) =&amp;gt; {
 matchPosts.push(postsByTitle[m.ref]);
 });

 if (matchPosts.length &amp;gt; 0) {
 $target.innerHTML = matchPosts.map(p =&amp;gt; {
 return `&amp;lt;div&amp;gt;
 &amp;lt;h3&amp;gt;&amp;lt;a href="${p.link}"&amp;gt;${p.title}&amp;lt;/a&amp;gt;&amp;lt;/h3&amp;gt;
 &amp;lt;p&amp;gt;${p.content}...&amp;lt;/p&amp;gt;
 &amp;lt;/div&amp;gt;`;
 }).join('');
 } else {
 $target.innerHTML = `&amp;lt;div&amp;gt;No search results found&amp;lt;/div&amp;gt;`;
 }
});
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can see Search in action at &lt;a href="https://codewithhugo.com/search/?q=lunrjs"&gt;codewithhugo.com/search/?q=lunrjs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;See the full gist at &lt;a href="https://gist.github.com/HugoDF/aac2e529f79cf90d2050d7183571684b"&gt;gist.github.com/HugoDF&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://unsplash.com/@ellladee?utm_medium=referral&amp;amp;utm_campaign=photographer-credit&amp;amp;utm_content=creditBadge"&gt;unsplash-logo&lt;br&gt;
N.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>jamstack</category>
      <category>node</category>
      <category>hugo</category>
    </item>
    <item>
      <title>JavaScript Data Type Check Cheatsheet</title>
      <dc:creator>Hugo Di Francesco</dc:creator>
      <pubDate>Mon, 24 Jun 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/hugo__df/javascript-data-type-check-cheatsheet-5b7d</link>
      <guid>https://dev.to/hugo__df/javascript-data-type-check-cheatsheet-5b7d</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;JavaScript (JS) is a lightweight, interpreted, or just-in-time compiled programming language with first-class functions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript"&gt;MDN Web Docs - JavaScript&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Detailed in this post are common JavaScript data type checks, pitfalls and idiomatic workarounds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Classic JavaScript Data Type Checks
&lt;/h2&gt;

&lt;p&gt;Here’s a collection of the most common data type checks in JavaScript. Whether you want to check if a variable contains a Date, a Promise, a plain old JavaScript object or an Array, it’s all here.&lt;/p&gt;

&lt;p&gt;Everything from primitive types like number, boolean, string to detecting functions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check if JavaScript variable contains an object
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;typeof&lt;/code&gt; does output &lt;code&gt;'object'&lt;/code&gt; for objects.&lt;/p&gt;

&lt;p&gt;It also does so for &lt;code&gt;null&lt;/code&gt; and Arrays.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const object = {};

console.log(typeof object); // 'object'
console.log(typeof null); // 'object'
console.log(typeof []); // 'object'

console.log(object instanceof Object); // true
console.log(null instanceof Object); // false
console.log([] instanceof Object); // true

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;What’s more, much like in the case of Arrays, if there is inter-frame communication, it tends to share objects and arrays (see &lt;a href="https://dev.to/hugo__df/detecting-object-vs-array-in-javascript-by-example-297d"&gt;JavaScript array type check - “is array” vs object in-depth&lt;/a&gt;). Therefore, checking whether something is a simple object vs an instance of a class is difficult.&lt;/p&gt;

&lt;p&gt;In JavaScript you’ll notice that everything is an Object, and when you try to access a property that doesn’t exist, it’ll fail quietly (ie. return &lt;code&gt;undefined&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;console.log('aaaaa'.noProperty); // undefined
console.log([].nothing); // undefined

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In idiomatic JavaScript code, we leverage this property to be just defensive enough, for example if we expect an object that has a &lt;code&gt;growl&lt;/code&gt; method, but something else might be passed in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function safeGrowl(anything) {
  if (anything.growl) {
    anything.growl()
  }
}

safeGrowl('hello'); // nothing
safeGrowl({ growl() { console.log('Growl!') }}); // Growl!

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The moral of the story is: don’t check that something is an object, check that it has the properties you need (that’s what’s called &lt;a href="https://en.wikipedia.org/wiki/Duck_typing"&gt;duck typing&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Check if a value is a string in JavaScript
&lt;/h3&gt;

&lt;p&gt;For strings, we can use a &lt;code&gt;typeof&lt;/code&gt; check.&lt;/p&gt;

&lt;p&gt;Much the same as for object checks, JavaScript won’t fail loudly when you try to use something as a string that isn’t a string, it will tend to just coerce it or call &lt;code&gt;.toString&lt;/code&gt; on it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const string = 'Hello World';
console.log(typeof string); // 'string'

// Implicit coercion to string using templates
const helloCount = 2;
const newString = `Hello number ${helloCount} at ${new Date('2019-06-23T21:00:26.861Z')}`;

console.log(newString);
// 'Hello number 2 at Sun Jun 23 2019 22:00:26 GMT+0100 (British Summer Time)'

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This works with dates, number. For arrays and other objects that don’t directly implement a toString method, I would suggest using JSON.stringify.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const myArray = ['a', 'b', 'c'];
const mySimpleObject = { key: 'value' };

console.log(`${myArray} ${mySimpleObject}`); // 'a,b,c [object Object]'
console.log(`${JSON.stringify(myArray)} ${JSON.stringify(mySimpleObject)}`)
// '["a","b","c"] {"key":"value"}'

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Check if a value is a JavaScript Number/Integer
&lt;/h3&gt;

&lt;p&gt;JavaScript Numbers are a bag of fun. They’ve got a similar gotcha to &lt;code&gt;object&lt;/code&gt; checks, that’s the &lt;code&gt;NaN&lt;/code&gt; (Not a Number) value. &lt;code&gt;NaN&lt;/code&gt; usually is the output of trying to do arithmetic where one of the operands is not a Number.&lt;/p&gt;

&lt;p&gt;The quirks of &lt;code&gt;NaN&lt;/code&gt; is that it’s not equal to itself, and it’s actually a Number, just like &lt;code&gt;Infinity&lt;/code&gt; and &lt;code&gt;- Infinity&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;console.log(NaN == NaN); // false
console.log(NaN === NaN); // false

console.log(typeof NaN); // 'number'
console.log(typeof Infinity); // 'number'
console.log(typeof -Infinity); // 'number'
console.log(typeof 123); // 'number'

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Checking that a Number is not &lt;code&gt;NaN&lt;/code&gt; (Not A Number)
&lt;/h4&gt;

&lt;p&gt;One &lt;code&gt;NaN&lt;/code&gt; check would just be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const a = NaN;

function isNotANumber(maybeNotANumber) {
  return maybeNotANumber === maybeNotANumber;
}

isNotANumber(a); // true

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The recommended approach is the following, there’s a built-in &lt;code&gt;Number.isNaN&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN('abc')); // false
console.log(Number.isNaN(1234)); // false
console.log(Number.isNaN(123.11)); // false
console.log(Number.isNaN(true)); // false

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The difference between &lt;code&gt;Number.isNaN&lt;/code&gt; and the &lt;code&gt;isNaN&lt;/code&gt; global is that &lt;code&gt;Number.isNaN&lt;/code&gt; checks that the value passed in is a Number &lt;em&gt;and&lt;/em&gt; it is &lt;code&gt;NaN&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The older global &lt;code&gt;isNaN&lt;/code&gt; function just goes for the literal check that something is not a number.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;console.log(isNaN(NaN)); // true
console.log(isNaN('abc')); // true
console.log(isNaN(1234)); // false
console.log(isNaN(123.11)); // false
console.log(isNaN(true)); // false

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Check if a JavaScript variable contains an Integer
&lt;/h4&gt;

&lt;p&gt;To check that JavaScript variable (or value) is an integer, we can use &lt;code&gt;Number.isInteger&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;console.log(Number.isInteger(123)); // true
console.log(Number.isInteger(12.3)); // false
console.log(Number.isInteger(123.0)); // true

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Check if a JavaScript variable contains a useable Number value
&lt;/h4&gt;

&lt;p&gt;To check that we’ve got a useable input value, we should check that the type is &lt;code&gt;number&lt;/code&gt; and that the value is not NaN:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function isValidNumber(maybeNumber) {
  return typeof maybeNumber === 'number' &amp;amp;&amp;amp; !Number.isNaN(maybeNumber);
}

console.log(isValidNumber('aaaaa')); // false
console.log(isValidNumber(NaN)); // false
console.log(isValidNumber(123)); // true
console.log(isValidNumber(1.23)); // true

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Check if a value is a boolean
&lt;/h3&gt;

&lt;p&gt;As with JavaScript string and number data types, in JavaScript the pattern is to assume something is a boolean (or cast it to boolean) rather than checking that it’s a boolean. That’s because in JavaScript we can use logical operators with non boolean values due to the loose typing, this is usually explained through the concept of “truthiness” and “falsiness”.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When JavaScript is expecting a boolean and it is given one of the values below, it will always evaluate to “falsy”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Falsy"&gt;MDN Web Docs - Falsy&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The values in question (falsy values) are: &lt;code&gt;false&lt;/code&gt;, &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;''&lt;/code&gt; (or other empty string), &lt;code&gt;null&lt;/code&gt; and &lt;code&gt;undefined&lt;/code&gt;. Any other value will be evaluated to true.&lt;/p&gt;

&lt;p&gt;There are some cases where &lt;code&gt;false&lt;/code&gt; means something other than &lt;code&gt;undefined&lt;/code&gt;, in that case, it’s possible to check that a value is falsy &lt;em&gt;and&lt;/em&gt; a boolean by using the &lt;code&gt;typeof&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;console.log(typeof true); // 'boolean'
console.log(typeof false); // 'boolean'

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Check if a variable contains an Array
&lt;/h3&gt;

&lt;p&gt;To check if a JavaScript variable is an Array, there’s a built-in &lt;code&gt;Array.isArray&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The fun gotcha with JavaScript Arrays is that they’re just objects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;console.log(([]) instanceof Object); // true
console.log(typeof []); // 'object'

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;A way to duck-type an Array is to use existence of a &lt;code&gt;.length&lt;/code&gt; property. However this can be pretty weak since there’s nothing enforcing that Arrays should be the only type to have a &lt;code&gt;.length&lt;/code&gt; property. The pattern tends to look like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function processList(maybeArray) {
  if (!maybeArray.length) {
    return []
  }
  return maybeArray.map(i =&amp;gt; i); // literally copy
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now this code doesn’t actually check that &lt;code&gt;maybeArray&lt;/code&gt; is an Array. It sort of does that but in the same line of code ie. &lt;code&gt;!maybeArray.length&lt;/code&gt;, it also states that &lt;code&gt;maybeArray&lt;/code&gt; must have a non-falsy length, ie. in the case where it &lt;em&gt;is&lt;/em&gt; in fact an Array, it shoul also not have length 0 (must not be empty).&lt;/p&gt;

&lt;p&gt;It’s trivial to trick the above and make it crash on &lt;code&gt;.map&lt;/code&gt; with for example using the following data: &lt;code&gt;{ length: 'aaaa' }&lt;/code&gt;. That’s not the point, if consumers of this function are trusted, this kind of check can be fine.&lt;/p&gt;

&lt;p&gt;Using Array.isArray however works as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;console.log(Array.isArray({})); // false
console.log(Array.isArray(new Map())); // false
console.log(Array.isArray(new Set())); // false

console.log(Array.isArray([])); // true
console.log(Array.isArray(new Array())); // true

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;For a closer look at the internals of how we check for Arrays, see &lt;a href="https://dev.to/hugo__df/detecting-object-vs-array-in-javascript-by-example-297d"&gt;JavaScript array type check - “is array” vs object in-depth&lt;/a&gt;. The big gotcha with built-in JavaScript data types like Array, Object and Date in JavaScript is that communicating between frames means the constructors and therefore &lt;code&gt;instanceof&lt;/code&gt; checks don’t work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check if an object is an instance of a specific class/constructor function
&lt;/h3&gt;

&lt;p&gt;Say you’ve got a set variable and you want to check that it’s a React Component, you can do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { Component } from 'react';

const myComp = new Component();

function isReactComponent(maybeComponent) {
  return maybeComponent instanceof Component;
}

isReactComponent(myComp);
// true

isReactComponent({});
// false

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This also works with constructor functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function Dog (name) {
  this.name = name
}

const max = new Dog('Max');

console.log(max instanceof Dog); // true

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Another interesting thing is it works all the way up the prototype chain/class hierarchy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;console.log(({}) instanceof Object); // true
console.log((new Dog) instanceof Object); // true

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Check if an object is an Error
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Error&lt;/code&gt; is just a constructor/class. So in the same way we could check for &lt;code&gt;React.Component&lt;/code&gt; or &lt;code&gt;Dog&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function isError(maybeError) {
  return maybeError instanceof Error;
}

isError(new Error('Something went wrong')); // true
isError(new EvalError()); // true
isError(new InternalError()); // true
isError(new RangeError()); // true
isError(new ReferenceError()); // true
isError(new SyntaxError()); // true
isError(new TypeError()); // true
isError(new URIError()); // true

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;See more about &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference#Fundamental_objects"&gt;Fundamental Objects on MDN&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check for a valid JavaScript Date string (parseable date-string)
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function isValidDateString(maybeDateString) {
  return !Number.isNaN(Number(new Date(maybeDateString)));
}

console.log(isValidDateString('abcd')); // false
console.log(isValidDateString(1234)); // true
console.log(isValidDateString('2019-06-23T22:00:26.861Z')); // true

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The above function doesn’t actually check whether something is a valid string but whether it’s convertible to a valid date.&lt;/p&gt;

&lt;p&gt;For most intents and purposes, it &lt;em&gt;will&lt;/em&gt; catch dodgy date strings, and has the benefit of not being unreadable at the cost of allow number timestamps to be passed in. A more apt name might be &lt;code&gt;isConvertibleToDate&lt;/code&gt;. Disallowing numbers would just be a case of adding a &lt;code&gt;typeof maybeDateString === 'string'&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check for a valid JavaScript Date
&lt;/h3&gt;

&lt;p&gt;To check whether or not something is a valid, we’ll just take the same approach as checking whether it’s convertible to a date&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function isValidDateObject(maybeDate) {
  return (
    typeof maybeDate === 'object' &amp;amp;&amp;amp;
    !Number.isNaN(Number(new Date(maybeDate))
  );
}

isValidDateObject('abc'); // false
isValidDateObject(1234); // false
isValidDateObject('2019-06-23T22:00:26.861Z'); // false
isValidDateObject(new Date('2019-06-23T22:00:26.861Z')); // true

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You could also apply the &lt;code&gt;instanceof&lt;/code&gt; approch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function isValidDateObject(maybeDate) {
  return maybeDate instanceof Date;
}

isValidDateObject('abc'); // false
isValidDateObject(1234); // false
isValidDateObject('2019-06-23T22:00:26.861Z'); // false
isValidDateObject(new Date('2019-06-23T22:00:26.861Z')); // true

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This has some cross-frame issues and you never know when someone might mess with the global &lt;code&gt;Date&lt;/code&gt; to replace it with their own custom, non-standard version.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check if a JavaScript variable is a Promise
&lt;/h3&gt;

&lt;p&gt;Promise checks are done using &lt;code&gt;instanceof&lt;/code&gt; with all the usual cross-frame or custom implementation caveats:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;console.log({} instanceof Promise); // false

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With Promises there’s also the issue for then-ables. Which for most intents and purposes may as well be Promises but which won’t pass our above check.&lt;/p&gt;

&lt;p&gt;In async/await-enabled environments, you’ll also note that &lt;code&gt;await&lt;/code&gt;-ing a function that doesn’t return a Promise doesn’t cause any unintended side-effects, the return value of the function (even if it’s not an async function), can be stored the same way as you would if you hadn’t &lt;code&gt;await&lt;/code&gt;-ed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check if a JavaScript variable is a function
&lt;/h3&gt;

&lt;p&gt;As mentioned on the MDN Web Docs JavaScript is a “programming language with &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/First-class_Function"&gt;first-class functions&lt;/a&gt;”. First-class functions, just means functions are treated like any other variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;console.log(typeof (() =&amp;gt; {})); // 'function'
console.log(typeof function () {}); // 'function'
function myFunc () {}
console.log(typeof myFunc); // 'function'

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Refer to the object example to see how an idiomatic “run this function if it exists” would look.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging JavaScript Data Type Issues
&lt;/h2&gt;

&lt;p&gt;What does each of the following look like when it’s &lt;code&gt;console.log&lt;/code&gt;-ed?&lt;/p&gt;

&lt;p&gt;One of the big issues is that &lt;code&gt;console.log&lt;/code&gt; tends to stringify whatever object it’s passed using its &lt;code&gt;.toString()&lt;/code&gt; method. A lot of the time, a combination of pattern matching, logging out &lt;code&gt;typeof&lt;/code&gt; and logging out a &lt;code&gt;JSON.stringify&lt;/code&gt;-ed version of the object gives good results.&lt;/p&gt;

&lt;h3&gt;
  
  
  Figuring out if something is a Promise
&lt;/h3&gt;

&lt;p&gt;Forgot to &lt;code&gt;await&lt;/code&gt; a function that returns a Promise (including an &lt;code&gt;async&lt;/code&gt; function).&lt;/p&gt;

&lt;p&gt;You usually want to do something with the output of the Promise:&lt;/p&gt;

&lt;h4&gt;
  
  
  A promise that’s supposed to return a list
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight hack"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;fetchList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'first-item'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;firstItem&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fetchList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;// UnhandledPromiseRejectionWarning:&lt;/span&gt;
&lt;span class="c1"&gt;// TypeError: fetchList is not a function or its return value is not iterable&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  A promise that’s supposed to return an object
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight hack"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;fetchObj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'value'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fetchObj&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'done'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;// undefined&lt;/span&gt;
&lt;span class="c1"&gt;// Promise {&lt;/span&gt;
&lt;span class="c1"&gt;// { property: 'value' },&lt;/span&gt;
&lt;span class="c1"&gt;// and so on&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Debugging Array vs Array-like
&lt;/h3&gt;

&lt;p&gt;There are a few Array-like objects in JavaScript for example &lt;code&gt;arguments&lt;/code&gt;, &lt;code&gt;NodeList&lt;/code&gt;s (output of &lt;code&gt;document.querySelectorAll&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The first thing to do is to just log them out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const anchors = document.querySelectorAll('a');
console.log(anchors); // { "0": {}, "1": {} }

function multiVariateFn() {
  console.log(arguments)
}

multiVariateFn(1, 2, 3); // [Arguments] { '0': 1, '1': 2, '2': 3 }

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Compare those outputs to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;console.log([1, 2, 3]); // [1, 2, 3]

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here is the old-school method of converting them to regular &lt;code&gt;Array&lt;/code&gt;-s (which means you can use Array.forEach/.map etc.):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const anchors = Array.prototype.slice.call(
  document.querySelectorAll('a'),
  0
);

function multiVariateFn() {
  const args = Array.prototype.slice.call(arguments, 0);
  return args.reverse();
}

console.log(multiVariateFn(1, 2, 3, 4)); // [4, 3, 2, 1]
console.log(multiVariateFn('a', 'b', 'c')); // ['c', 'b', 'a']

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The ES6 approach would look something closer to this, they take advantage of array spread syntax and rest parameters syntax respectively.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const anchors = [...document.querySelectorAll('a')];

function multiVariateFn(...args) {
  return args.reverse();
}

console.log(multiVariateFn(1, 2, 3, 4)); // [4, 3, 2, 1]
console.log(multiVariateFn('a', 'b', 'c')); // ['c', 'b', 'a']

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;A more conservative approach could leverage &lt;code&gt;Array.from&lt;/code&gt; (see &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from"&gt;Array from MDN Web Docs&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const anchors = Array.from(document.querySelectorAll('a'));

function multiVariateFn() {
  return Array.from(arguments).reverse();
}

console.log(multiVariateFn(1, 2, 3, 4)); // [4, 3, 2, 1]
console.log(multiVariateFn('a', 'b', 'c')); // ['c', 'b', 'a']

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Source Materials - Further Reading
&lt;/h2&gt;

&lt;p&gt;While creating this JavaScript Data Type Check guide, I took inspiration from some of the top relevant posts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://tobyho.com/2011/01/28/checking-types-in-javascript/"&gt;http://tobyho.com/2011/01/28/checking-types-in-javascript/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://webbjocke.com/javascript-check-data-types/"&gt;https://webbjocke.com/javascript-check-data-types/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ultimatecourses.com/blog/understanding-javascript-types-and-reliable-type-checking"&gt;https://ultimatecourses.com/blog/understanding-javascript-types-and-reliable-type-checking&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As well as the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript"&gt;MDN Web Docs for JavaScript&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://unsplash.com/@danielfazio?utm_medium=referral&amp;amp;utm_campaign=photographer-credit&amp;amp;utm_content=creditBadge"&gt;unsplash-logo&lt;br&gt;
Daniel Fazio&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>datatypes</category>
      <category>es6</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Tailwind CSS 1.x impressions, development and production setup for static sites with Tailwind CLI and PurgeCSS CLI</title>
      <dc:creator>Hugo Di Francesco</dc:creator>
      <pubDate>Mon, 17 Jun 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/hugo__df/tailwind-css-1-x-impressions-development-and-production-setup-for-static-sites-with-tailwind-cli-and-purgecss-cli-6en</link>
      <guid>https://dev.to/hugo__df/tailwind-css-1-x-impressions-development-and-production-setup-for-static-sites-with-tailwind-cli-and-purgecss-cli-6en</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;How to set Tailwind CSS up with a static site hosted on Netlify&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;See the full starter repository at &lt;a href="https://github.com/HugoDF/netlify-lambda-tailwind-static-starter"&gt;github.com/HugoDF/netlify-lambda-tailwind-static-starter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tailwindcss.com/"&gt;Tailwind CSS&lt;/a&gt; is “a utility-first CSS framework for rapidly building custom designs”. I’ll be walking through my first impressions of it as well as how to leverage its CLI (and the PurgeCSS CLI) to add it to any project without a build process (eg. Webpack, Gulp).&lt;/p&gt;

&lt;h2&gt;
  
  
  First Impressions of Tailwind CSS
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;p&gt;The integrated CLI tool means you don’t have to set up your own build step. I’ve found that for development, running the full tailwind build (no purging of styles), is beneficial, so much so that I’ve added it as the &lt;code&gt;prestart&lt;/code&gt; npm script (means it runs before &lt;code&gt;yarn start&lt;/code&gt; or &lt;code&gt;npm start&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Tailwind 1.x leverages PostCSS instead of being SCSS or stylus based. Despite the massive stride forward node-sass has been for frontend tooling, the fact that it &lt;em&gt;is&lt;/em&gt; a linked dependency can cause issues when changing Node versions or using different Operating Systems.&lt;/p&gt;

&lt;p&gt;It’s a breath of fresh air to &lt;em&gt;not&lt;/em&gt; have to set up node-sass, a skill which you forget as soon as you’ve got the build up and running.&lt;/p&gt;

&lt;p&gt;Tailwind’s utility-first approach means I barely even write any CSS. I managed to quickly map my CSS knowledge onto the utility classes which makes it great for quickly gettting a prototype up. A great read on how to get over the “why would anyone do this” moment of utility classes, is Adam Wathan’s &lt;a href="https://adamwathan.me/css-utility-classes-and-separation-of-concerns/"&gt;CSS Utility Classes and “Separation of Concerns”&lt;/a&gt; .&lt;/p&gt;

&lt;p&gt;Once the few command line tools necessary for a productive Tailwind 1.x setup are stringed together (namely PurgeCSS) it feels great.&lt;/p&gt;

&lt;p&gt;The copy-pastable component library is also great. Unlike Bootstrap, Foundation or even some of the more modern toolkits like Material UI or Bulma, none of the Tailwind CSS components rely on JavaScript. It’s a CSS framework after all. So the components are more of a cookbook/recipe that are meant to be copy-pasted and modified.&lt;/p&gt;

&lt;p&gt;Tailwind 1.x pushes a pre-defined colour palette and rationalises what sizes are allowed.No more pixel-slinging, there’s just a scale: &lt;code&gt;1 2 3 4 5 6 8 10 12 16 20 24 32 40 48 56 64&lt;/code&gt;,you can read more about the Tailwind CSS scale in the &lt;a href="https://tailwindcss.com/docs/customizing-spacing#default-spacing-scale"&gt;Default spacing scale documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;p&gt;The default bundle size is quite large, this can be offset using &lt;a href="https://www.purgecss.com/#cli"&gt;PurgeCSS&lt;/a&gt; or by configuring the build (see &lt;a href="https://tailwindcss.com/docs/controlling-file-size"&gt;Controlling File Size in the Tailwind CSS documentation&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;PurgeCSS CLI can be a pain to set up, and you have to whitelist liberally to get Tailwind CSS modifier utils. That’s all pretty well documented though.&lt;/p&gt;

&lt;p&gt;The repetition involved with using a utility-class based framework like Tailwind CSS is a bit tedious to begin with.&lt;/p&gt;

&lt;p&gt;1.x also introduced a bunch of extra places where you need to repeat yourself, all the links need to have a &lt;code&gt;class="text-blue-500 hover:text-blue-800”&lt;/code&gt;, all the headings have to manually sized. In the long run, this is great since these are the kind of styles that would keep getting overwritten, so why not leave them unstyled from the start.&lt;/p&gt;

&lt;p&gt;There’s no more SCSS, no more CSS even, it’s all utility classes and that can get to you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verdict
&lt;/h3&gt;

&lt;p&gt;I’m a fan, it’s nice and orthogonal to all my other stack choices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I can use it with an SPA setup&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I can use it with static (just HTML) demos on Netlify eg. Netlify Pocket Lambda demo&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I can use with a server-page setup&lt;/p&gt;

&lt;p&gt;With the command-line tool, it’s even language agnostic. I don’t need to add npm/Node to the deployed environment, I can pre-build, use the CDN version or even check it into git.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up Tailwind CSS v1.x for a static site with Yarn, Tailwind CLI and PurgeCSS CLI
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Getting up and running with Tailwind CLI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add --dev tailwindcss
# or
npm install --save-dev tailwindcss
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following to &lt;code&gt;package.json&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "scripts": {
    "build:css": "yarn build:tw",
    "build:tw": "tailwind build src/styles.css -o public/styles.css",
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Brutally prune CSS rules with PurgeCSS CLI
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt; : This is the naive way of doing CSS purging.It doesn’t take into consideration that we’re using Tailwind CSS.Hence when using this approach, some utilities won’t work.See PurgeCSS configuration for Tailwind for the right way to do it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add --dev purgecss
# or
npm install --save-dev purgecss
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following to &lt;code&gt;package.json&lt;/code&gt;.We’re leveraging npm scripts “post”-hook to run PurgeCSS against the output of the Tailwind CLI.&lt;/p&gt;

&lt;p&gt;It works since &lt;code&gt;build:css&lt;/code&gt; is the Tailwind CLI generation command.&lt;/p&gt;

&lt;p&gt;We use the &lt;code&gt;--css&lt;/code&gt; argument to point PurgeCSS to the generated styles and the &lt;code&gt;--content&lt;/code&gt; argument to point to the HTML that we’re purging against.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;--out&lt;/code&gt; flag uses the same directory as the initial (pre-PurgeCSS) styles.css, which means we’re overwriting it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "scripts": {
    "postbuild:css": "purgecss --css public/styles.css --content public/index.html --out public"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting up for development
&lt;/h3&gt;

&lt;p&gt;For development we want to use a full Tailwind CSS build.&lt;/p&gt;

&lt;p&gt;In order to do that, we’ll leverage the fact that we tend to run application using the &lt;code&gt;start&lt;/code&gt; script.&lt;/p&gt;

&lt;p&gt;Once again, we’ll leverage npm script hooks, in this case a “pre”-hook, to run a full Tailwind CSS v1.x build.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;prestart&lt;/code&gt; script simply calls &lt;code&gt;yarn build:tw&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Add the following to your scripts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"prestart": "yarn build:tw",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when you run &lt;code&gt;yarn start&lt;/code&gt;, before the &lt;code&gt;start&lt;/code&gt; script is run, a full Tailwind build will be run.In the case of the Netlify starter project at &lt;a href="https://github.com/HugoDF/netlify-lambda-tailwind-static-starter"&gt;github.com/HugoDF/netlify-lambda-tailwind-static-starter&lt;/a&gt;, the &lt;code&gt;start&lt;/code&gt; command use &lt;code&gt;netlify dev&lt;/code&gt; ie. &lt;a href="https://dev.to/scottw/netlify-dev-3je3-temp-slug-5267792"&gt;Netlify Dev&lt;/a&gt; which is supposed to run the site just as it would when it gets deployed to Netlify.&lt;/p&gt;

&lt;h3&gt;
  
  
  Production PurgeCSS configuration for Tailwind
&lt;/h3&gt;

&lt;p&gt;As mentioned in Brutally prune CSS rules with PurgeCSS CLI, that naive approach doesn’t work.&lt;/p&gt;

&lt;p&gt;Here’s how to set PurgeCSS CLI up properly to be Tailwind v1.x aware, it hinges on the using a Tailwind CSS specific extractor (which is just a RegEx).&lt;/p&gt;

&lt;p&gt;We’re leveraging the extractors option as &lt;a href="https://www.purgecss.com/configuration#configuration-file"&gt;documented under PurgeCSS “Configuration File”&lt;/a&gt;, see the following &lt;code&gt;purgecss.config.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = {
  content: ['path/to/content.html'],
  css: ['path/to/styles.css'],
  extractors: [
    {
      extractor: {
        extract: content =&amp;gt; content.match(/[A-z0-9-:\/]+/g)
      },
      extensions: ['html']
    }
  ]
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And update the &lt;code&gt;postbuild:css&lt;/code&gt; script to the following in order to use the &lt;code&gt;purgecss.config.js&lt;/code&gt; file and still output to &lt;code&gt;public&lt;/code&gt; directory (thereby overwriting the intiail Tailwind CLI output).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"postbuild:css": "purgecss -c purgecss.config.js -o public"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Get started with a production-ready Tailwind CSS for static sites on Netlify
&lt;/h2&gt;

&lt;p&gt;See and use the full starter repository at &lt;a href="https://github.com/HugoDF/netlify-lambda-tailwind-static-starter"&gt;github.com/HugoDF/netlify-lambda-tailwind-static-starter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://unsplash.com/@schwiet?utm_medium=referral&amp;amp;utm_campaign=photographer-credit&amp;amp;utm_content=creditBadge"&gt;unsplash-logo&lt;br&gt;
seth schwiet&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tailwindcss</category>
      <category>css</category>
      <category>frontend</category>
      <category>jamstack</category>
    </item>
    <item>
      <title>Jest exclude file/function/statement from test coverage</title>
      <dc:creator>Hugo Di Francesco</dc:creator>
      <pubDate>Mon, 10 Jun 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/hugo__df/jest-exclude-file-function-statement-from-test-coverage-b1b</link>
      <guid>https://dev.to/hugo__df/jest-exclude-file-function-statement-from-test-coverage-b1b</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;In computer science, test coverage is a measure used to describe the degree to which the source code of a program is executed when a particular test suite runs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Code_coverage"&gt;Code coverage - Wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Code coverage is usually used as a quality metric for software eg. “Our code has to have 80%+ test coverage”. Gathering test coverage with Jest is as simple as using the &lt;code&gt;--coverage&lt;/code&gt; flag on invocation.&lt;/p&gt;

&lt;p&gt;This post goes through how to ignore files, functions and statements from coverage in Jest using configuration or istanbul pragmas. As well as reasons and limits to why/how you would do such a thing.&lt;/p&gt;

&lt;p&gt;You can find a working examples repository at &lt;a href="https://github.com/HugoDF/jest-ignore-coverage"&gt;github.com/HugoDF/jest-ignore-coverage&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does Jest even calculate coverage?
&lt;/h2&gt;

&lt;p&gt;Jest uses &lt;a href="https://github.com/istanbuljs/istanbuljs"&gt;istanbul&lt;/a&gt; under the hood to calculate coverage. Mostly Jest abstracts this from the end user, all you have to do in your application is call &lt;code&gt;jest --coverage&lt;/code&gt; (and configured the appropriate coverage configuration fields). The fact that istanbul is used internally does show, for example, the &lt;a href="https://jestjs.io/docs/en/configuration.html#coveragereporters-array-string"&gt;documentation for coverageReporters&lt;/a&gt; mentions that “Any &lt;a href="https://github.com/istanbuljs/istanbuljs/tree/master/packages/istanbul-reports/lib"&gt;istanbul reporter&lt;/a&gt; can be used.”, which shows what’s actually collecting coverage data and generating the reports.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why would I want to exclude files from coverage?
&lt;/h2&gt;

&lt;p&gt;As stated by the maintainers and authors of the istanbul coverage libary:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Some branches in JS code are typically hard, if not impossible to test.Examples are a hasOwnProperty check, UMD wrappers and so on. Istanbul now has a facility by which coverage can be excluded for certain sections of code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/gotwarlost/istanbul/blob/master/ignoring-code-for-coverage.md#ignoring-code-for-coverage-purposes"&gt;Istanbul - Ignore code for coverage purposes&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What’s more, 100% coverage isn’t necessary or even reasonable in most cases. Some files don’t contain any (business) logic. Or they contain logic that would fail in a very obvious way (eg. a starter crashing on start).&lt;/p&gt;

&lt;p&gt;For example the script that would bootstrap an application might bind to a port, which makes it unwieldy to test. The file that imports all the different dependencies and &lt;code&gt;app.use()&lt;/code&gt;-es them in an Express setting would be another candidate for maybe avoiding the unit testing/dependency mocking hell.&lt;/p&gt;

&lt;p&gt;Another class of files/functions you might want to ignore for coverage purposes are test-specific helpers. It doesn’t matter that some of them aren’t run as part of tests, as they’re not the code under test.&lt;/p&gt;

&lt;p&gt;As with a lot of things in software, it’s about tradeoffs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exclude file(s) from Jest coverage by not running relevant tests using configuration
&lt;/h2&gt;

&lt;p&gt;There’s a Jest configuration option &lt;code&gt;testPathIgnorePatterns&lt;/code&gt; (&lt;a href="https://jestjs.io/docs/en/configuration.html#testpathignorepatterns-array-string"&gt;see the docs for testPathIgnorePatterns&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;The simplest way to configure this is through the &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"jest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"testPathIgnorePatterns"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;rootDir&amp;gt;/ignore/this/path/"&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;See it in action at &lt;a href="https://github.com/HugoDF/jest-ignore-coverage#exclude-files-from-jest-coverage-using-configuration"&gt;Exclude file(s) from Jest coverage using configuration on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exclude file(s) from coverage by not including it in the coverage collection configuration
&lt;/h2&gt;

&lt;p&gt;As an alternative or augmentation to not running tests (as seen in “Exclude file from Jest coverage by not running relevant tests using configuration”) from Jest coverage by not including it in coverage reports, that’s controlled by the &lt;code&gt;collectCoverageFrom&lt;/code&gt; Jest configuration option (&lt;a href="https://jestjs.io/docs/en/configuration.html#collectcoveragefrom-array"&gt;see the docs for Jest collectCoverageFrom&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Use something like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"jest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"collectCoverageFrom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"src/**/{!(ignore-me),}.js"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt; : make sure to wrap the ignored file’s name with &lt;code&gt;()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;See it in action at &lt;a href="https://github.com/HugoDF/jest-ignore-coverage#exclude-files-from-jest-coverage-using-configuration"&gt;Exclude file(s) from Jest coverage using configuration on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exclude file from Jest coverage at the file level
&lt;/h2&gt;

&lt;p&gt;We can use istanbul pragmas to ignore files using the following comment at the top of any file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* istanbul ignore file */&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;See it in action at &lt;a href="https://github.com/HugoDF/jest-ignore-coverage#exclude-file-from-jest-coverage-at-the-file-level"&gt;Exclude file from Jest coverage at the file level on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Exclude function from Jest coverage
&lt;/h2&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* istanbul ignore next */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;myFunc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Not covered but won't appear on coverage reports as such&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;See it in action at &lt;a href="https://github.com/HugoDF/jest-ignore-coverage#exclude-function-or-statement-from-jest-coverage"&gt;Exclude function or statement from Jest coverage on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exclude statement from Jest coverage
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Avoid this if you can, if you’re testing some code you should probably test&lt;/strong&gt;  &lt;strong&gt;&lt;em&gt;all&lt;/em&gt;&lt;/strong&gt;  &lt;strong&gt;of that code.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;myFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* istanbul ignore else */&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// do some work&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// do some other work&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;See it in action at &lt;a href="https://github.com/HugoDF/jest-ignore-coverage#exclude-function-or-statement-from-jest-coverage"&gt;Exclude function or statement from Jest coverage on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;p&gt;See the &lt;a href="https://github.com/gotwarlost/istanbul/blob/master/ignoring-code-for-coverage.md"&gt;original istanbul documentation on ignoring code for coverage&lt;/a&gt; for a more extensive look at how to do this in different situations.&lt;/p&gt;

&lt;p&gt;I’ve also put a together an examples repo with all the different cases in action &lt;a href="https://github.com/HugoDF/jest-ignore-coverage"&gt;github.com/HugoDF/jest-ignore-coverage&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://unsplash.com/@charlesdeluvio?utm_medium=referral&amp;amp;utm_campaign=photographer-credit&amp;amp;utm_content=creditBadge"&gt;unsplash-logo&lt;br&gt;
Charles 🇵🇭&lt;/a&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>jest</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How long is a new feature/page expected to take?</title>
      <dc:creator>Hugo Di Francesco</dc:creator>
      <pubDate>Sun, 09 Jun 2019 11:33:34 +0000</pubDate>
      <link>https://dev.to/hugo__df/how-long-is-a-new-feature-page-expected-to-take-4074</link>
      <guid>https://dev.to/hugo__df/how-long-is-a-new-feature-page-expected-to-take-4074</guid>
      <description>&lt;p&gt;One of my mentees just asked me &lt;br&gt;
"When working for a company, what is the expected timeline for a new feature/page?"&lt;/p&gt;

&lt;p&gt;What would your answer be?&lt;/p&gt;

&lt;p&gt;I've created a poll on Twitter if you're more into that: &lt;a href="https://twitter.com/hugo__df/status/1137683451593863168?s=20"&gt;https://twitter.com/hugo__df/status/1137683451593863168?s=20&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>discuss</category>
      <category>career</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Super-powered newsletter content with Pocket and Netlify Lambda</title>
      <dc:creator>Hugo Di Francesco</dc:creator>
      <pubDate>Mon, 03 Jun 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/hugo__df/super-powered-newsletter-content-with-pocket-and-netlify-lambda-2nce</link>
      <guid>https://dev.to/hugo__df/super-powered-newsletter-content-with-pocket-and-netlify-lambda-2nce</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;An example Netlify Lambda to fetch all “newsletter” posts from &lt;a href="https://getpocket.com"&gt;Pocket&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Pocket_(service)"&gt;Pocket is an application and web service for managing a reading list of articles from the Internet&lt;/a&gt;. It's quite widely used and tightly integrated into the Firefox browser. I find that I use it extensively to save articles (usually about development).&lt;/p&gt;

&lt;p&gt;For the &lt;a href="https://buttondown.email/hugo"&gt;Enterprise Node.js and JavaScript newsletter&lt;/a&gt; I have a “From the Web” section, which is populated from links from the web. Since most links that end up there are at one point or another stored on Pocket, I built a lambda that fetches posts tagged with “newsletter” from Pocket. I then consume that lambda from a &lt;a href="https://gohugo.io"&gt;Hugo&lt;/a&gt; newsletter file generator.&lt;/p&gt;

&lt;p&gt;See the lambda code at &lt;a href="https://github.com/HugoDF/pocket-newsletter-lambda/blob/master/src/lambda/newsletter.js"&gt;src/lambda/newsletter.js&lt;/a&gt; in the repository &lt;a href="https://github.com/HugoDF/pocket-newsletter-lambda"&gt;github.com/HugoDF/pocket-newsletter-lambda&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Run it at &lt;a href="https://pocket-newsletter-lambda.netlify.com/"&gt;https://pocket-newsletter-lambda.netlify.com/&lt;/a&gt;, or even &lt;a href="(https://app.netlify.com/start/deploy?repository=https://github.com/HugoDF/pocket-newsletter-lambda)"&gt;deploy your own on Netlify&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The application is styled using TailwindCSS, you can see a starter project for that at &lt;a href="https://github.com/HugoDF/netlify-lambda-tailwind-static-starter"&gt;github.com/HugoDF/netlify-lambda-tailwind-static-starter&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pocket Fetching logic
&lt;/h2&gt;

&lt;p&gt;The bulk of the Pocket-specific logic is the &lt;code&gt;fetchBookmarks&lt;/code&gt; function, it does the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fetch from Pocket API using consumer key and access token

&lt;ul&gt;
&lt;li&gt;passes &lt;code&gt;state: 'all'&lt;/code&gt; in order to get both archived and unarchived posts&lt;/li&gt;
&lt;li&gt;uses &lt;code&gt;tag: 'newsletter'&lt;/code&gt; to fetch only posts tagged with &lt;code&gt;newsletter&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;detailType: 'complete'&lt;/code&gt; means the API returns more complete data&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;convert the response to a flat list of &lt;code&gt;{ title, url, excerpts, authors }&lt;/code&gt; (all of those fields are strings)&lt;/li&gt;
&lt;li&gt;return the list&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See the code (full source at &lt;a href="https://github.com/HugoDF/pocket-newsletter-lambda"&gt;github.com/HugoDF/pocket-newsletter-lambda&lt;/a&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;fetchBookmarks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;consumerKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://getpocket.com/v3/get&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;consumer_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;consumerKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;newsletter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;all&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;detailType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;complete&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// List is a key-value timestamp-&amp;gt;entry map&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;given_title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;given_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;resolved_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;resolved_title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;authors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;rest&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;given_title&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;resolved_title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;given_url&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;resolved_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;authors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authors&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;authors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Netlify Lambda request validation and body-parsing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  HTTP verb and payload presence validation
&lt;/h3&gt;

&lt;p&gt;The lambda only supports POST with a body, hence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;httpMethod&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Not Found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bad Request&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Parsing POST request bodies from form submissions and AJAX/JSON requests
&lt;/h3&gt;

&lt;p&gt;We support both URL-encoded form POST requests (done eg. when JS is disabled on the demo page) and JSON requests.&lt;/p&gt;

&lt;p&gt;The body arrives either base64 encoded (if using a URL-encoded form body request) or not. This is denoted by the &lt;code&gt;isBase64Encoded&lt;/code&gt; flag on the &lt;code&gt;event&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Parsing a base64-encoded string in Node is done using &lt;code&gt;Buffer.from(event.body, 'base64').toString('utf-8)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To convert the body from URL-encoded form into an object, the following function is used, which works for POSTs with simple fields.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;parseUrlEncoded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;urlEncodedString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keyValuePairs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;urlEncodedString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;amp;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;keyValuePairs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;kvPairString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;kvPairString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here's the functionality in the lambda:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;pocket_consumer_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pocketConsumerKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;pocket_access_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pocketAccessToken&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isBase64Encoded&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;parseUrlEncoded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf-8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If the consumer key or access token are missing we send a 400:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;pocketConsumerKey&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;pocketAccessToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bad Request&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Sending appropriate responses on failure
&lt;/h3&gt;

&lt;p&gt;We attempt to &lt;code&gt;fetchBookmarks&lt;/code&gt;, this functionality has been broken down in “Pocket Fetching logic”.&lt;/p&gt;

&lt;p&gt;When the Pocket API fails on a request error we want to send back that response's information to the client. If the failure can't be identified, 500. When &lt;code&gt;fetchBookmarks&lt;/code&gt; succeeds send a 200 with data.&lt;/p&gt;

&lt;p&gt;Thankfully when axios fails, it has a &lt;code&gt;response&lt;/code&gt; property on the error. This means that our “Proxy back Pocket API errors” use-case and the other 2 cases are easily fulfilled with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bookmarks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetchBookmarks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pocketConsumerKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pocketAccessToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bookmarks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Error while connecting to Pocket API: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusText&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Sample response
&lt;/h2&gt;

&lt;p&gt;See &lt;a href="https://github.com/HugoDF/pocket-newsletter-lambda#sample-response"&gt;github.com/HugoDF/pocket-newsletter-lambda#sample-response&lt;/a&gt; or try out the application yourself at &lt;a href="https://pocket-newsletter-lambda.netlify.com/"&gt;pocket-newsletter-lambda.netlify.com/&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Pocket-driven newsletter in the real-world
&lt;/h2&gt;

&lt;p&gt;On &lt;a href="https://codewithhugo.com"&gt;codewithhugo.com&lt;/a&gt;, the lambda doesn't read the access token and consumer key from the request. Instead it's a GET endpoints that reads the token and key from environment variables.&lt;/p&gt;

&lt;p&gt;It's actually simpler to do that. We set &lt;code&gt;POCKET_CONSUMER_KEY&lt;/code&gt; and &lt;code&gt;POCKET_ACCESS_TOKEN&lt;/code&gt; in the Netlify build configuration. Then update the lambda to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;POCKET_CONSUMER_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;POCKET_ACCESS_TOKEN&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// keep fetchBookmarks as is&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;httpMethod&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Not Found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bookmarks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetchBookmarks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;POCKET_CONSUMER_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;POCKET_ACCESS_TOKEN&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bookmarks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://codewithhugo.com"&gt;codewithhugo.com&lt;/a&gt; runs on &lt;a href="https://gohugo.io"&gt;Hugo&lt;/a&gt;. To generate a new newsletter file, I leverage &lt;a href="https://gohugo.io/content-management/archetypes/"&gt;Hugo custom archetypes&lt;/a&gt; (ie. a content type that has a template to generate it).&lt;/p&gt;

&lt;p&gt;The archetype looks something like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;replace&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;replaceRE&lt;/span&gt; &lt;span class="s"&gt;`[0-9]{4}-[0-9]{2}-[0-9]{2}-`&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="s"&gt;"-"&lt;/span&gt; &lt;span class="s"&gt;" "&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="o"&gt;---&lt;/span&gt;
&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Code&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;Hugo&lt;/span&gt;
&lt;span class="o"&gt;---&lt;/span&gt;

&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;newsletterBookmarks&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;getJSON&lt;/span&gt; &lt;span class="s"&gt;"https://codewithhugo.com/.netlify/functions/newsletter"&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;newsletterBookmarks&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="p"&gt;[{{&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="p"&gt;}}]({{&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}})&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;authors&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;excerpt&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Once the newsletter has been generated and edited, it gets added to &lt;a href="https://buttondown.email"&gt;buttondown.email&lt;/a&gt;, you can see the result of this approach at the &lt;a href="https://buttondown.email/hugo/archive"&gt;Enterprise Node.js and JavaScript Archives&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://unsplash.com/@iammrcup?utm_medium=referral&amp;amp;utm_campaign=photographer-credit&amp;amp;utm_content=creditBadge"&gt;unsplash-logo&lt;br&gt;
Mr Cup / Fabien Barral&lt;/a&gt;&lt;/p&gt;

</description>
      <category>jamstack</category>
      <category>serverless</category>
      <category>javascript</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Avoiding recursion pitfalls: MongoDB nested query clause addition and removal</title>
      <dc:creator>Hugo Di Francesco</dc:creator>
      <pubDate>Mon, 20 May 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/hugo__df/avoiding-recursion-pitfalls-mongodb-nested-query-clause-addition-and-removal-3n83</link>
      <guid>https://dev.to/hugo__df/avoiding-recursion-pitfalls-mongodb-nested-query-clause-addition-and-removal-3n83</guid>
      <description>&lt;p&gt;A case-study on where recursion can be useful for enterprise Node.js applications and how to avoid its common pitfalls like &lt;code&gt;RangeError: Maximum call stack size exceeded&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The full repository for this post is on GitHub: &lt;a href="https://github.com/HugoDF/mongo-query-clause-modification"&gt;github.com/HugoDF/mongo-query-clause-modification&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’ll be implementing a solution to 2 real-world problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add an $or query clause to a MongoDB query&lt;/li&gt;
&lt;li&gt;Remove references to a field in an MongoDB query (potentially) using $or and $and&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Add an $or query clause to a MongoDB query
&lt;/h2&gt;

&lt;p&gt;See the final code at &lt;a href="https://github.com/HugoDF/mongo-query-clause-modification/blob/master/src/add-or-clause.js"&gt;./src/add-or-clause.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The parameters are &lt;code&gt;query&lt;/code&gt; and &lt;code&gt;orClause&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;query&lt;/code&gt; is a MongoDB query which might or might not already contain an &lt;code&gt;$or&lt;/code&gt; and/or &lt;code&gt;$and&lt;/code&gt; clause.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;orClause&lt;/code&gt; is an object containing and &lt;code&gt;$or&lt;/code&gt; clause (it’s a fully-fledged MongoDB query in its own right) eg.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orClause&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;$exists&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;someDate&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There is initially just 1 thing to look out for:1. the query does not contain an $or clause2. the query contains an $or clause&lt;/p&gt;

&lt;h3&gt;
  
  
  When there’s no $or clause in the query
&lt;/h3&gt;

&lt;p&gt;If there is no &lt;code&gt;$or&lt;/code&gt; clause, we can simply spread our &lt;code&gt;orClause&lt;/code&gt; query and the &lt;code&gt;query&lt;/code&gt; parameter, ie.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;orClause&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That is unless there’s and &lt;code&gt;$and&lt;/code&gt; in there somewhere, in which case we want to add our &lt;code&gt;orClause&lt;/code&gt; to the &lt;code&gt;$and&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;$and&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$and&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orClause&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  When there’s an $or clause in the query
&lt;/h3&gt;

&lt;p&gt;If there is an &lt;code&gt;$or&lt;/code&gt; clause, we can’t just overwrite it, we need to &lt;code&gt;$and&lt;/code&gt; the two &lt;code&gt;$or&lt;/code&gt; queries.&lt;/p&gt;

&lt;p&gt;We should also keep existing &lt;code&gt;$and&lt;/code&gt; clause contents which yields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;queryWithoutOrRemoved&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;$and&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$and&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]),&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$or&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;orClause&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Full solution
&lt;/h3&gt;

&lt;p&gt;This is also available at &lt;a href="https://github.com/HugoDF/mongo-query-clause-modification/blob/master/src/add-or-clause.js"&gt;./src/add-or-clause.js&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;addOrClause&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orClause&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;queryRest&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;queryRest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;$and&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...(&lt;/span&gt;&lt;span class="nx"&gt;queryRest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$and&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;orClause&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queryRest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$and&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;queryRest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;$and&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;queryRest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$and&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orClause&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;orClause&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;addOrClause&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Corresponding Test Suite
&lt;/h3&gt;

&lt;p&gt;We can observe how the different cases map pretty directly to test cases.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addOrClause&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./add-or-clause&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should add the passed or clause if no $or on the current query&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orClause&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;myField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;myField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}]};&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addOrClause&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orClause&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;myField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;myField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;when the query already has an $or&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should add the passed or clause to and $and that also contains the current query&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orClause&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;myField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;myField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}]};&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;$exists&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;}}]};&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addOrClause&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orClause&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;$and&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;$exists&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;}}]},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;myField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;myField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;when the query has an $and&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should keep the $and, add the $or and the current query&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orClause&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;myField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;myField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}]};&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;world&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
        &lt;span class="na"&gt;$and&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;baz&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addOrClause&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orClause&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;$and&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;baz&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;world&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}]},&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;myField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;myField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}]}&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;when the query has an $and query&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should add the new or clause to the $and&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orClause&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;myField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;myField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}]};&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;$and&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;baz&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}]};&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addOrClause&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orClause&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;$and&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;baz&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;myField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;myField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}]}&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Remove references to a field in an MongoDB query (potentially) using $or and $and
&lt;/h2&gt;

&lt;p&gt;See the full solution at &lt;a href="https://github.com/HugoDF/mongo-query-clause-modification/blob/master/src/remove-field-references.js"&gt;./src/remove-field-references.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this case we’re creating a function that takes 2 parameters: &lt;code&gt;query&lt;/code&gt; (MongoDB query as above) and &lt;code&gt;fieldName&lt;/code&gt; (name of the field we want to remove references to).&lt;/p&gt;

&lt;h3&gt;
  
  
  Remove top-level fields
&lt;/h3&gt;

&lt;p&gt;The simplest thing to do is remove references to the field at the top-level of the object.&lt;/p&gt;

&lt;p&gt;We can create a simple &lt;code&gt;omit&lt;/code&gt; function using destructuring and recursion&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;omit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;nextFields&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{[&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;ignore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;nextFields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;omit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nextFields&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;omit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;fieldName&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Remove fields in any $or clause
&lt;/h3&gt;

&lt;p&gt;To remove fields in an $or clause (which is a fully-fledged query) is as simple as taking the $or value (which is an array) and running a recursion of the function onto it.&lt;/p&gt;

&lt;p&gt;This will remove fields at the top-level of the &lt;code&gt;$or&lt;/code&gt; sub-queries &lt;em&gt;and&lt;/em&gt; in nest &lt;code&gt;$or&lt;/code&gt; fields’ sub-queries.&lt;/p&gt;

&lt;p&gt;We want to make sure to remove empty $or sub-queries, since &lt;code&gt;{ $or: [{ }, {}]}&lt;/code&gt; is an invalid query.&lt;/p&gt;

&lt;p&gt;We default the query’s &lt;code&gt;$or&lt;/code&gt; to an empty array and check length before spreading it back into the newQuery. This is because &lt;code&gt;{ $or: [] }&lt;/code&gt; is an invalid query.&lt;/p&gt;

&lt;p&gt;We’re also careful to remove the top-level &lt;code&gt;$or&lt;/code&gt; when spreading &lt;code&gt;filteredTopLevel&lt;/code&gt; so that if the new &lt;code&gt;$or&lt;/code&gt; is an empty array, the old &lt;code&gt;$or&lt;/code&gt; is ommitted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;removeFieldReferences&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fieldName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filteredTopLevel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;omit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;fieldName&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newOr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filteredTopLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$or&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;removeFieldReferences&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fieldName&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;omit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filteredTopLevel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$or&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;newOr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newOr&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Remove fields in any $and clause
&lt;/h3&gt;

&lt;p&gt;The rationale for the &lt;code&gt;$and&lt;/code&gt; solution is the same as for the $or solution.&lt;/p&gt;

&lt;p&gt;We recurse and check that we’re not generating an invalid query by omitting empty arrays and objects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;removeFieldReferences&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fieldName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filteredTopLevel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;omit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;fieldName&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newAnd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filteredTopLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$and&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;removeFieldReferences&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fieldName&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;omit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filteredTopLevel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$and&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;newAnd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;$and&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newAnd&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Check that we’re not likely to bust the stack
&lt;/h3&gt;

&lt;p&gt;The actual implementation has a &lt;code&gt;maxDepth&lt;/code&gt; 3rd parameter defaulted to 5.&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;maxDepth&lt;/code&gt; is equal to &lt;code&gt;0&lt;/code&gt;, we return the query without any treatment (arguably we should run the top-level filter).&lt;/p&gt;

&lt;p&gt;On recursive calls to &lt;code&gt;removeFieldReferences&lt;/code&gt; we pass &lt;code&gt;(q, fieldName, maxDepth - 1)&lt;/code&gt; so that we’re not going any deeper than we need to by accident.&lt;/p&gt;

&lt;p&gt;This avoids &lt;code&gt;RangeError: Maximum call stack size exceeded&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Implementation
&lt;/h3&gt;

&lt;p&gt;This is also available at &lt;a href="https://github.com/HugoDF/mongo-query-clause-modification/blob/master/src/remove-field-references.js"&gt;./src/remove-field-references.js&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;omit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;nextFields&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{[&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;ignore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;nextFields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;omit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nextFields&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;removeFieldReferences&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fieldName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxDepth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxDepth&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filteredTopLevel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;omit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;fieldName&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newOr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filteredTopLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$or&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;removeFieldReferences&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fieldName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxDepth&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newAnd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filteredTopLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$and&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;removeFieldReferences&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fieldName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxDepth&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;omit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filteredTopLevel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$or&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$and&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;newOr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newOr&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}),&lt;/span&gt;
    &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;newAnd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;$and&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newAnd&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;removeFieldReferences&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Corresponding Test Suite
&lt;/h3&gt;

&lt;p&gt;We can observe how the different cases map pretty directly to test cases.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;removeFieldReferences&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./remove-field-references&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should remove top-level fields&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fieldName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;removeFieldReferences&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fieldName&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBeUndefined&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return passed query when maxDepth is hit (avoids busting the stack by default)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fieldName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;removeFieldReferences&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fieldName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should remove references to the field in top-level $or queries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;otherField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not-related&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;othervalue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;otherField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;even-less-related&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fieldName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;removeFieldReferences&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fieldName&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;otherField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not-related&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;otherField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;even-less-related&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should remove $or clauses where the query becomes empty on omission of a field&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;otherField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not-related&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fieldName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;removeFieldReferences&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fieldName&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;otherField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not-related&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should remove references to field in top-level queries inside of $and&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;$and&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;otherField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;other-value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;otherField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fieldName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;removeFieldReferences&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fieldName&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;$and&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;otherField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;otherField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should remove $and clause if all queries end up filtered out&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;$and&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;other-value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fieldName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;removeFieldReferences&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fieldName&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should remove references to field in nested $or inside of $and&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;$and&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;otherField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not-related&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fieldName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;removeFieldReferences&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fieldName&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;$and&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;otherField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not-related&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://unsplash.com/@tine999?utm_medium=referral&amp;amp;utm_campaign=photographer-credit&amp;amp;utm_content=creditBadge"&gt;unsplash-logo&lt;br&gt;
Tine Ivanič&lt;/a&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>node</category>
      <category>mongodb</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Read and parse POST/PATCH/PUT request JSON or form body with Express and no dependencies</title>
      <dc:creator>Hugo Di Francesco</dc:creator>
      <pubDate>Sat, 11 May 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/hugo__df/read-and-parse-post-patch-put-request-json-or-form-body-with-express-and-no-dependencies-3meh</link>
      <guid>https://dev.to/hugo__df/read-and-parse-post-patch-put-request-json-or-form-body-with-express-and-no-dependencies-3meh</guid>
      <description>&lt;p&gt;When asked to handle data in a request body, developers who have used Express (the “Fast, unopinionated, minimalist web framework for &lt;a href="https://nodejs.org/en/"&gt;Node.js&lt;/a&gt;”) before, reach for the &lt;a href="https://github.com/expressjs/body-parser"&gt;body-parser&lt;/a&gt; library.&lt;/p&gt;

&lt;p&gt;What they might not know is that body-parse is a dependency of Express and its main JSON parsing and url encoded body parsing functionality is exposed as &lt;code&gt;express.json()&lt;/code&gt; and &lt;code&gt;express.urlencoded()&lt;/code&gt;. A middleware for parsing JSON request bodies built-into Express.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: &lt;code&gt;express.json&lt;/code&gt; and &lt;code&gt;express.urlencoded&lt;/code&gt; exist in Express 4.16+&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The global &lt;code&gt;express.json()&lt;/code&gt; middleware is not to be confused with the &lt;code&gt;res.json()&lt;/code&gt; function which sends back a body as JSON (and sets the content format headers to JSON).&lt;/p&gt;

&lt;p&gt;The code for json bodies is at &lt;a href="https://codesandbox.io/s/olrn6x3n19?fontsize=14"&gt;https://codesandbox.io/s/olrn6x3n19?fontsize=14&lt;/a&gt;, you can view the app at &lt;a href="https://olrn6x3n19.sse.codesandbox.io/"&gt;https://olrn6x3n19.sse.codesandbox.io/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The code for URL encoded form bodies is at &lt;a href="https://codesandbox.io/s/6njqzmyw4k?fontsize=14"&gt;https://codesandbox.io/s/6njqzmyw4k?fontsize=14&lt;/a&gt;, you can test it out at &lt;a href="https://6njqzmyw4k.sse.codesandbox.io/"&gt;https://6njqzmyw4k.sse.codesandbox.io/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Real(ish) world usage in projects, you can also &lt;a href="https://codewithhugo.com/tags/express"&gt;find more Express posts on Code with Hugo&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;express.urlencoded&lt;/code&gt;: &lt;a href="https://github.com/HugoDF/express-nunjucks-tailwind/blob/master/server.js"&gt;https://github.com/HugoDF/express-nunjucks-tailwind/blob/master/server.js&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;express.json&lt;/code&gt;: &lt;a href="https://github.com/HugoDF/express-postgres-starter/blob/master/server.js"&gt;https://github.com/HugoDF/express-postgres-starter/blob/master/server.js&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Parsing JSON bodies
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Drop-in example
&lt;/h3&gt;

&lt;p&gt;Here is an example app that reflects the request POST body in the response:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'express'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s1"&gt;'/test'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Server listening on port &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can see it running at &lt;a href="https://olrn6x3n19.sse.codesandbox.io/"&gt;https://olrn6x3n19.sse.codesandbox.io/&lt;/a&gt;, test it using:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; https://olrn6x3n19.sse.codesandbox.io/test &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'content-type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
    "json-parsing": "just with Express",
    "no": "body-parser"
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It will output:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"json-parsing"&lt;/span&gt;:&lt;span class="s2"&gt;"just with Express"&lt;/span&gt;,&lt;span class="s2"&gt;"no"&lt;/span&gt;:&lt;span class="s2"&gt;"body-parser"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Which is exactly what we sent in the &lt;code&gt;--data&lt;/code&gt; property of the curl request.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extended configuration options
&lt;/h3&gt;

&lt;p&gt;You can pass options into it like so (all the values are the default values for these options):&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;inflate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'100kb'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;reviver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'application/json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;
&lt;span class="p"&gt;}))&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The full API docs for &lt;code&gt;express.json&lt;/code&gt; are at &lt;a href="https://expressjs.com/en/api.html#express.json"&gt;expressjs.com/en/api.html#express.json&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A run-through of the options follows.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;inflate&lt;/code&gt; controls whether or not to handle compressed/deflated request bodies. When it’s set to false, compressed/deflated bodies will get rejected.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;limit&lt;/code&gt; controls the maximum body size. It can be either a number of bytes or a string which is compatible with the &lt;a href="https://www.npmjs.com/package/bytes"&gt;bytes&lt;/a&gt; library.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;strict&lt;/code&gt; is about locking JSON.parse down to just objects and arrays. If true, only &lt;code&gt;JSON.stringify&lt;/code&gt;-ed objects and arrays will be allowed, if false, anything that JSON.parse accepts will be parsed.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;type&lt;/code&gt; controls which content-type the middleware will attempt to parse. The value for this option can be a string, an array of strings, or a function. Content-type wildcards are supported to some extent since string(s) are passed to the &lt;a href="https://www.npmjs.com/package/type-is"&gt;type-is&lt;/a&gt; library.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;verify&lt;/code&gt; is an optional function with &lt;code&gt;verify(req, res, buf, encoding)&lt;/code&gt; signature. &lt;code&gt;buf&lt;/code&gt; is a Buffer containing the raw request body. &lt;code&gt;verify&lt;/code&gt; can be used to abort parsing by throwing an error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parsing URL encoded form bodies
&lt;/h2&gt;

&lt;p&gt;In the same way we used &lt;code&gt;express.json&lt;/code&gt; we can use &lt;code&gt;express.urlencoded&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Drop-in example
&lt;/h3&gt;

&lt;p&gt;We pass &lt;code&gt;extended: false&lt;/code&gt; to avoid a warning.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'express'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urlencoded&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;extended&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s1"&gt;'/test'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Server listening on port &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can see it running at &lt;a href="https://6njqzmyw4k.sse.codesandbox.io/"&gt;https://6njqzmyw4k.sse.codesandbox.io/&lt;/a&gt;, test it using the form on the page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extended configuration options
&lt;/h3&gt;

&lt;p&gt;You can pass options into &lt;code&gt;express.urlencoded&lt;/code&gt; like so (all the values are the default values for these options):&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;extended&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;inflate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'100kb'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;parameterLimit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'application/x-www-form-urlencoded'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;
&lt;span class="p"&gt;}))&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The full API docs for &lt;code&gt;express.json&lt;/code&gt; are at &lt;a href="https://expressjs.com/en/api.html#express.urlencoded"&gt;expressjs.com/en/api.html#express.urlencoded&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A run-through of the options follows.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;extended&lt;/code&gt; pretty much a toggle between the &lt;code&gt;qs&lt;/code&gt; (when &lt;code&gt;true&lt;/code&gt;) and &lt;code&gt;querystring&lt;/code&gt; (when &lt;code&gt;false&lt;/code&gt;) libraries. &lt;code&gt;qs&lt;/code&gt; allows for rich objects and arrays to be encoded, &lt;code&gt;querystring&lt;/code&gt; does not. Keep this as &lt;code&gt;false&lt;/code&gt; for simple forms (all key-value pairs) and &lt;code&gt;true&lt;/code&gt; when you’re sending arrays/maps of values.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;inflate&lt;/code&gt; controls whether or not to handle compressed/deflated request bodies. When it’s set to false, compressed/deflated bodies will get rejected.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;limit&lt;/code&gt; controls the maximum body size. It can be either a number of bytes or a string which is compatible with the &lt;a href="https://www.npmjs.com/package/bytes"&gt;bytes&lt;/a&gt; library.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;parameterLimit&lt;/code&gt; controls the maximum number of fields that we’ll attempt to parse from the url encoded form body.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;type&lt;/code&gt; controls which content-type the middleware will attempt to parse. The value for this option can be a string, an array of strings, or a function. Content-type wildcards are supported to some extent since string(s) are passed to the &lt;a href="https://www.npmjs.com/package/type-is"&gt;type-is&lt;/a&gt; library.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;verify&lt;/code&gt; is an optional function with &lt;code&gt;verify(req, res, buf, encoding)&lt;/code&gt; signature. &lt;code&gt;buf&lt;/code&gt; is a Buffer containing the raw request body. &lt;code&gt;verify&lt;/code&gt; can be used to abort parsing by throwing an error.&lt;/p&gt;

&lt;h2&gt;
  
  
  More about body-parser and other body-parsing libraries
&lt;/h2&gt;

&lt;p&gt;This means we don’t need to install body-parser to load up most of our bodies. Express’ built-in JSON and URL encoded form body parsing covers the majority of use cases. What we might need a library for is multipart bodies, there are alternative libraries to handle that use-case (usually for file upload).&lt;/p&gt;

&lt;p&gt;There are still some very specific cases where body-parser might still be necessary (see the docs at &lt;a href="https://github.com/expressjs/body-parser"&gt;github.com/expressjs/body-parser&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/expressjs/body-parser#bodyparserjsonoptions"&gt;JSON body parser&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/expressjs/body-parser#bodyparserrawoptions"&gt;Raw body parser&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/expressjs/body-parser#bodyparsertextoptions"&gt;Text body parser&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/expressjs/body-parser#bodyparserurlencodedoptions"&gt;URL-encoded form body parser&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://unsplash.com/@mahiruysal?utm_medium=referral&amp;amp;utm_campaign=photographer-credit&amp;amp;utm_content=creditBadge"&gt;unsplash-logo&lt;br&gt;
Mahir Uysal&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>express</category>
      <category>bodyparser</category>
    </item>
    <item>
      <title>Jest assert over single or specific argument/parameters with .toHaveBeenCalledWith and expect.anything()</title>
      <dc:creator>Hugo Di Francesco</dc:creator>
      <pubDate>Mon, 29 Apr 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/hugo__df/jest-assert-over-single-or-specific-argument-parameters-with-tohavebeencalledwith-and-expect-anything-4caj</link>
      <guid>https://dev.to/hugo__df/jest-assert-over-single-or-specific-argument-parameters-with-tohavebeencalledwith-and-expect-anything-4caj</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;With Jest it’s possible to assert of single or specific arguments/parameters of a mock function call with &lt;code&gt;.toHaveBeenCalled&lt;/code&gt;/&lt;code&gt;.toBeCalled&lt;/code&gt; and &lt;code&gt;expect.anything()&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The full example repository is at &lt;a href="https://github.com/HugoDF/jest-specific-argument-assert"&gt;github.com/HugoDF/jest-specific-argument-assert&lt;/a&gt;, more specifically &lt;a href="https://github.com/HugoDF/jest-specific-argument-assert/blob/master/src/pinger.test.js#L17-L66"&gt;lines 17-66 in the src/pinger.test.js file&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can use &lt;code&gt;expect.anything()&lt;/code&gt; to ignore certain parameters that a mock Jest function is called with, see the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;calls getPingConfigs with right accountId, searchRegex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pinger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockPingConfig&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;anything&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;anything&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Read on for more details of the code under test and why one would use such an approach.&lt;/p&gt;

&lt;p&gt;The code under test follows module boundaries similar to what is described in &lt;a href="https://dev.to/hugo__df/an-enterprise-style-node-js-rest-api-setup-with-docker-compose-express-and-postgres-2hm0-temp-slug-2013945"&gt;An enterprise-style Node.js REST API setup with Docker Compose, Express and Postgres&lt;/a&gt;. Specifically a &lt;a href="https://codewithhugo.com/node-postgres-express-docker-compose/#architecture-example-user-session-management"&gt;3-tier (Presentation, Domain, Data) layering&lt;/a&gt;, where we’ve only implemented the domain and (fake) data layers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code under test that warrants specific parameter/argument assertions
&lt;/h2&gt;

&lt;p&gt;The code under test is the following (&lt;a href="https://github.com/HugoDF/jest-specific-argument-assert/blob/master/src/pinger.js"&gt;see the full src/pinger.js file on GitHub&lt;/a&gt;), only relevant code has been included to make it obvious what problem we’ll be tackling with Jest mocks, &lt;code&gt;.toHaveBeenCalled&lt;/code&gt; and &lt;code&gt;expect.anything()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Half-baked implementation of an uptime monitor&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getPingConfigs&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./pingConfig&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getUrlsForAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;searchRegex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;configs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;getPingConfigs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;searchRegex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// return configs.map(conf =&amp;gt; conf.url);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;pinger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;searchRegex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;search&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;|&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;getUrlsForAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;searchRegex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pinger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The only call going outside the module’s private context is &lt;code&gt;getPingConfigs(accountId, offset, limit, searchRegex)&lt;/code&gt;. This is why the assertion is going to be on the &lt;code&gt;getPingConfigs&lt;/code&gt; mock that we’ve set with &lt;code&gt;jest.mock('./pingConfig', () =&amp;gt; {})&lt;/code&gt; (&lt;a href="https://github.com/HugoDF/jest-specific-argument-assert/blob/master/src/pinger.test.js"&gt;see the full src/pinger.test.js code on GitHub&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Discovering orthogonality in code under test
&lt;/h3&gt;

&lt;p&gt;We can also see that there’s orthogonal functionality going on. Namely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;passing of &lt;code&gt;accountId&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;computing/defaulting/passing of a search regex&lt;/li&gt;
&lt;li&gt;defaulting/passing of offset/limit&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Issues with exhaustive test cases for orthogonal functionality
&lt;/h3&gt;

&lt;p&gt;All our tests will center around the values &lt;code&gt;getPingConfigs&lt;/code&gt; is called with (using &lt;code&gt;.toHaveBeenCalledWith&lt;/code&gt; assertions).&lt;/p&gt;

&lt;p&gt;Let’s create some tests that don’t leverage &lt;code&gt;expect.anything()&lt;/code&gt;, in every call, we’ll specify the value each of the parameters to &lt;code&gt;getPingConfigs&lt;/code&gt;: &lt;code&gt;accountId&lt;/code&gt;, &lt;code&gt;offset&lt;/code&gt;, &lt;code&gt;limit&lt;/code&gt; and &lt;code&gt;searchRegex&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Permutations, (&lt;code&gt;Y&lt;/code&gt; denotes the variable passed to &lt;code&gt;pinger&lt;/code&gt; is set, &lt;code&gt;N&lt;/code&gt; that it is not).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;accountId&lt;/th&gt;
&lt;th&gt;offset&lt;/th&gt;
&lt;th&gt;limit&lt;/th&gt;
&lt;th&gt;search&lt;/th&gt;
&lt;th&gt;single-word search&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;N&lt;/td&gt;
&lt;td&gt;N&lt;/td&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;N&lt;/td&gt;
&lt;td&gt;N&lt;/td&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;N&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;N&lt;/td&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;N&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;N&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;N&lt;/td&gt;
&lt;td&gt;N&lt;/td&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;N&lt;/td&gt;
&lt;td&gt;N&lt;/td&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;N&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;N&lt;/td&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;N&lt;/td&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;N&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;N&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each of the above permutations should lead to different test cases if we have to specify each of the parameters/arguments in the assertion on the &lt;code&gt;getPingConfigs&lt;/code&gt; call.&lt;/p&gt;

&lt;p&gt;The enumeration we’ve done above would result in &lt;strong&gt;10 test cases&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating test cases for orthogonal functionality
&lt;/h3&gt;

&lt;p&gt;It turns out the following cases cover the same logic in a way that &lt;em&gt;we care about&lt;/em&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;on search

&lt;ol&gt;
&lt;li&gt;if search is &lt;strong&gt;not&lt;/strong&gt; set, &lt;code&gt;pinger&lt;/code&gt; should call with the default searchRegex&lt;/li&gt;
&lt;li&gt;if search is set and is single word (no space), &lt;code&gt;pinger&lt;/code&gt; should call with the correct searchRegex&lt;/li&gt;
&lt;li&gt;if search is set and is multi-work (spaces), &lt;code&gt;pinger&lt;/code&gt; should call with the correct searchRegex&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;li&gt;on limit/offset

&lt;ol&gt;
&lt;li&gt;if limit/offset are &lt;strong&gt;not&lt;/strong&gt; set, &lt;code&gt;pinger&lt;/code&gt; should call with default values&lt;/li&gt;
&lt;li&gt;if limit/offset are set, &lt;code&gt;pinger&lt;/code&gt; should call with passed values&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Notice how the assertions only concern &lt;em&gt;part&lt;/em&gt; of the call, which is where &lt;code&gt;expect.anything()&lt;/code&gt; is going to come handy as a way to not have to assert over all the parameters/arguments of a mock call at the same time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Specific parameter asserts on a mock function call
&lt;/h2&gt;

&lt;p&gt;The following implements the test cases we’ve defined in &lt;a href="https://codewithhugo.com/jest-specific-argument-parameter-assert/#creating-test-cases-for-orthogonal-functionality"&gt;“Creating test cases for orthogonal functionality”&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;without search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;calls getPingConfigs with right accountId, searchRegex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pinger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockPingConfig&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;anything&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;anything&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;offset, limit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;calls getPingConfigs with passed offset and limit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pinger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockPingConfig&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;anything&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;calls getPingConfigs with default offset and limit if undefined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pinger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockPingConfig&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;anything&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;single-word search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;calls getPingConfigs with right accountId, searchRegex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pinger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockPingConfig&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;anything&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;anything&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;multi-word search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;calls getPingConfigs with right accountId, searchRegex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pinger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;multi word search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockPingConfig&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;anything&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;anything&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;multi|word|search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;p&gt;Head over to &lt;a href="https://github.com/HugoDF/jest-specific-argument-assert"&gt;github.com/HugoDF/jest-specific-argument-assert&lt;/a&gt; to see the full code and test suite. This includes code and tests that aren’t relevant to illustrate the concept of specific argument/parameter assertions with Jest &lt;code&gt;.toHaveBeenCalledWith&lt;/code&gt;/&lt;code&gt;.toBeCalled&lt;/code&gt; and &lt;code&gt;expect.anything()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The way the code is written loosely follows what is described in &lt;a href="https://dev.to/hugo__df/an-enterprise-style-node-js-rest-api-setup-with-docker-compose-express-and-postgres-2hm0-temp-slug-2013945"&gt;An enterprise-style Node.js REST API setup with Docker Compose, Express and Postgres&lt;/a&gt;. Specifically a &lt;a href="https://codewithhugo.com/node-postgres-express-docker-compose/#architecture-example-user-session-management"&gt;3-tier (Presentation, Domain, Data) layering&lt;/a&gt;, where we’ve only implemented the domain and (fake) data layers.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>testing</category>
      <category>jest</category>
      <category>tdd</category>
    </item>
  </channel>
</rss>
