<?xml version="1.0" encoding="utf-8" standalone="yes"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title></title>
  <subtitle></subtitle>
  <id>https://www.endpointdev.com/blog/tags/nodejs/</id>
  <link href="https://www.endpointdev.com/blog/tags/nodejs/"/>
  <link href="https://www.endpointdev.com/blog/tags/nodejs/" rel="self"/>
  <updated>2025-10-15T00:00:00+00:00</updated>
  <author>
    <name>End Point Dev</name>
  </author>
  
    <entry>
      <title>A Preact Web App Without npm Build</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2025/10/preact-web-app-without-npm-build/"/>
      <id>https://www.endpointdev.com/blog/2025/10/preact-web-app-without-npm-build/</id>
      <published>2025-10-15T00:00:00+00:00</published>
      <author>
        <name>Dmitry Kiselev</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2025/10/preact-web-app-without-npm-build/nyc-skyline-orange-sunset.webp&#34; alt=&#34;An orange sun sets over New York City, viewed from the top of a building in Manhattan. A water tower is visible in front of buildings which reflect bright light on the left side, with shadows on the right side.&#34;&gt;&lt;/p&gt;
&lt;!-- Photo by Seth Jensen, 2025. --&gt;
&lt;p&gt;Modern JavaScript frameworks such as React and Preact usually require a full “build pipeline” with Node.js, npm, and bundlers like Webpack or Vite. That’s fine for large web apps, but sometimes you just need a small, self-contained browser interface inside a project that isn’t mainly about the web — say, a Java program that processes transit data and simply needs a page to display results.&lt;/p&gt;
&lt;p&gt;This post explores how to use Preact and its companion library @preact/signals &lt;em&gt;without any npm build process at all&lt;/em&gt;. By taking advantage of browser-native import maps and writing a bit of code directly in JavaScript instead of JSX, it shows how to build a lightweight, modern UI that runs straight from static HTML, CSS, and JS files — no build tools required.&lt;/p&gt;
&lt;h3 id=&#34;the-scenario&#34;&gt;The Scenario&lt;/h3&gt;
&lt;p&gt;This might look like a strange problem to solve but sometimes the web UI part just isn’t a main character in your project.&lt;/p&gt;
&lt;p&gt;For instance, I have a GTFS data processor whose main goal is to preprocess public transport stop timetables. It’s written in Java and I want a simple browser UI to check its output. So I want the web UI part of the project to be just static HTML, CSS, and JavaScript files.&lt;/p&gt;
&lt;p&gt;I also don’t want to run npm or yarn inside what would otherwise be a pure Maven-managed project. But at the same time, I would like to use a modern JS UI framework such as Preact.&lt;/p&gt;
&lt;h3 id=&#34;the-main-issues&#34;&gt;The Main Issues&lt;/h3&gt;
&lt;p&gt;To pull off this trick, there are two main challenges:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JSX&lt;/li&gt;
&lt;li&gt;Imports&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;jsx&#34;&gt;JSX&lt;/h3&gt;
&lt;p&gt;JSX is basically JavaScript templates. When you write something like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; Greeting() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;div className=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;greeting&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      Hello world
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;/div&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; App() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; (&amp;lt;Greeting/&amp;gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;During project build Babel will transform it into:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; Greeting() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;// Particular function for creating elements may vary.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; h(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;div&amp;#39;&lt;/span&gt;, {className: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;greeting&amp;#34;&lt;/span&gt;}, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Hello world&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; App() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; h(Greeting, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;null&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is a pretty straightforward transformation, and no one is stopping you from importing the element creation function (h in the example above) and composing what would be Babel output yourself.&lt;/p&gt;
&lt;h3 id=&#34;imports&#34;&gt;Imports&lt;/h3&gt;
&lt;p&gt;Now for the more interesting issue: imports.&lt;/p&gt;
&lt;p&gt;Normally, with a build process, you could just write:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; { h } from &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;preact&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; { signal } from &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;@preact/signals&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;What’s more, some packages will try to resolve their own dependencies by importing them by global name. For instance, &lt;code&gt;@preact/signals&lt;/code&gt; will try to import &lt;code&gt;@preact/signals-core&lt;/code&gt;, and there isn’t a ready-made UMD (Universal Module Definition) version of &lt;code&gt;@preact/signals-core&lt;/code&gt; available.&lt;/p&gt;
&lt;h3 id=&#34;the-solution-browser-import-maps&#34;&gt;The Solution: Browser Import Maps&lt;/h3&gt;
&lt;p&gt;I would like to be able to just map library names to URLs of ES modules.&lt;/p&gt;
&lt;p&gt;There is a relatively new browser feature that does exactly this: an &lt;em&gt;import map&lt;/em&gt;. It’s a special type of &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; block containing a JSON object with your mapping.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-react&#34; data-lang=&#34;react&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;type&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;importmap&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;imports&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;preact&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://unpkg.com/preact@latest/dist/preact.mjs&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;preact/hooks&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://unpkg.com/preact@latest/hooks/dist/hooks.mjs&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;preact/compat&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://unpkg.com/preact@latest/compat/dist/compat.mjs&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;@preact/signals-core&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://unpkg.com/@preact/signals-core@latest/dist/signals-core.mjs&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;@preact/signals&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://unpkg.com/@preact/signals@latest/dist/signals.mjs&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And that’s it!&lt;/p&gt;
&lt;p&gt;With the import map in place, your browser can resolve all those imports directly from a CDN like &lt;a href=&#34;https://unpkg.com/&#34;&gt;unpkg.com&lt;/a&gt;. You can then use Preact, hooks, and signals exactly as if you were running a fully bundled app.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;This approach is ideal for small, embedded, or hybrid projects where a full JavaScript toolchain would be overkill. It lets you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Build modern UI components with Preact&lt;/li&gt;
&lt;li&gt;Avoid npm, yarn, Babel, and bundlers entirely&lt;/li&gt;
&lt;li&gt;Keep your project clean and language-native (e.g., Maven for Java, without extra layers)&lt;/li&gt;
&lt;li&gt;Run everything as plain static assets, right from your browser&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Everything works, and nothing extra gets in the way.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>How to write end-to-end &amp; component tests with Cypress in Vue.js</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2022/12/how-to-use-cypress-for-ui-testing/"/>
      <id>https://www.endpointdev.com/blog/2022/12/how-to-use-cypress-for-ui-testing/</id>
      <published>2022-12-10T00:00:00+00:00</published>
      <author>
        <name>Edgar Mlowe</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2022/12/how-to-use-cypress-for-ui-testing/beach-zanzibar.webp&#34; alt=&#34;A white beach in Zanzibar, Tanzania during a hot sunny day. A few trees provide shade for the beach, looking out at a light blue ocean.&#34;&gt;&lt;/p&gt;
&lt;!-- Photo by Edgar Mlowe, 2018 --&gt;
&lt;p&gt;Is writing tests painful for you? In this tutorial, I explain how to handle UI testing with &lt;a href=&#34;https://www.cypress.io&#34;&gt;Cypress&lt;/a&gt; and hope to convince you that writing tests is not always so tedious and expensive, but can be fun instead.&lt;/p&gt;
&lt;p&gt;Cypress is a purely JavaScript-based front-end testing tool built for the modern web. it can test anything that runs in a browser and has built-in support for testing modern frameworks such as Vue.js, React, and Angular. See the &lt;a href=&#34;https://docs.cypress.io/guides/component-testing/overview#Supported-Frameworks&#34;&gt;full list of front-end frameworks Cypress supports&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As an example we are going to use a to-do app built using Vue. We will learn:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How to install and set up Cypress.&lt;/li&gt;
&lt;li&gt;How to create a simple to-do app with Vue 3.&lt;/li&gt;
&lt;li&gt;How to write end-to-end tests.&lt;/li&gt;
&lt;li&gt;How to write component tests.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;how-to-install-and-set-up-cypress&#34;&gt;How to install and set up Cypress&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;First let&amp;rsquo;s create a new Vue project using the Vue CLI.&lt;/p&gt;
&lt;p&gt;Install Vue CLI if you don&amp;rsquo;t have it in your machine:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm install -g @vue/cli&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a project (pick the &lt;code&gt;Vue 3,babel,eslint&lt;/code&gt; preset):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vue create todo-app&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;cd&lt;/code&gt; into the &lt;code&gt;todo-app&lt;/code&gt; project and install Cypress:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm install cypress --save-dev&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;No dependencies, extra downloads, or changes to your code are required!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Edit &lt;code&gt;package.json&lt;/code&gt;. In the &lt;code&gt;scripts&lt;/code&gt; section, add a command, &lt;code&gt;&amp;quot;cypress:open&amp;quot;: &amp;quot;cypress open&amp;quot;&lt;/code&gt;. See the example below.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;scripts&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;cypress:open&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;cypress open&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Launch Cypress:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm run cypress:open&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When Cypress opens up you should be able to view the Cypress launchpad as shown in this screenshot:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/12/how-to-use-cypress-for-ui-testing/cypress-launcher.webp&#34; alt=&#34;Screenshot of Cypress launcher window displaying testing options. The header reads &amp;ldquo;todo-app (master)&amp;rdquo;. The body reads &amp;ldquo;Welcome to Cypress!&amp;rdquo;, and has a link reading &amp;ldquo;Review the differences between each testing type&amp;rdquo;. Two boxes read &amp;ldquo;E2E Testing&amp;rdquo; and &amp;ldquo;Component Testing&amp;rdquo;, both with buttons reading &amp;ldquo;Not Configured&amp;rdquo; next to an unfilled circle.&#34;&gt;&lt;/p&gt;
&lt;p&gt;We will use the Launchpad to configure both E2E Testing and Component Testing. Cypress will automatically generate configuration files as you configure the tests in the Launchpad. If you get stuck, please visit the &lt;a href=&#34;https://docs.cypress.io/guides/getting-started/opening-the-app#The-Launchpad&#34;&gt;docs for Launchpad&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;how-to-create-a-simple-to-do-app-with-vue-3&#34;&gt;How to create a simple to-do app with Vue 3&lt;/h3&gt;
&lt;p&gt;Open the &lt;code&gt;todo-app&lt;/code&gt; project in your favourite editor and add the following single-file components:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;src/components/BaseTextInput.vue&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;BaseTextInput will contain a text input and a button for adding new to-do items.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;template&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;form&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;add-todo-form&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;input&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;type&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;input&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;placeholder&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Add a new todo&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;v-model&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;todo&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;input&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;type&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;submit&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;value&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Add&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;@&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;click&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;emitNewTodoEvent&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;form&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;template&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;default&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    data() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            todo: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    methods: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        emitNewTodoEvent(e) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            e.preventDefault();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;this&lt;/span&gt;.$emit(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;newTodo&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;this&lt;/span&gt;.todo);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;this&lt;/span&gt;.todo = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;style&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;scoped&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;input&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;%&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;padding&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;8&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;10&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;border&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;solid&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;#32485F&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;add-todo-form&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;%&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;display&lt;/span&gt;: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;flex&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;align-items&lt;/span&gt;: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;center&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;input&lt;/span&gt;[&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;type&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;submit&amp;#34;&lt;/span&gt;] {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;margin-left&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;5&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;padding&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;8&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;10&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;border&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;solid&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;#32485F&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-color&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;#32485F&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;color&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;#fff&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;font-weight&lt;/span&gt;: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;bold&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;cursor&lt;/span&gt;: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;pointer&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;input&lt;/span&gt;[&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;type&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;submit&amp;#34;&lt;/span&gt;]:&lt;span style=&#34;color:#555&#34;&gt;hover&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-color&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;#00C185&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;style&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;src/components/TodoListItem.vue&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;TodoListItem displays a to-do along with a button for removing that to-do.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;template&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;li&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {{ todo.text }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;button&lt;/span&gt; &lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;@&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;click&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;$emit(&amp;#39;remove&amp;#39;, todo.id)&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        X
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;button&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;li&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;template&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;default&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    props: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      todo: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        type: &lt;span style=&#34;color:#038&#34;&gt;Object&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        required: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;src/components/TodoList.vue&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;TodoList component lists all to-dos.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;template&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;BaseTextInput&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;@&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;newTodo&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;addTodo&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ul&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;v-if&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;todos.length&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;TodoListItem&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;v-for&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;todo in todos&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;:key&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;todo.id&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;:todo&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;todo&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;@&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;remove&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;removeTodo&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ul&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;p&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;empty-state-message&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;v-else&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      Nothing left in the list. Add a new todo in the input above.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;p&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;template&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; BaseTextInput from &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;./BaseTextInput.vue&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; TodoListItem from &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;./TodoListItem.vue&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; nextTodoId = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;default&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  components: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BaseTextInput, TodoListItem
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  data () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      todos: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  methods: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    addTodo (todo) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; trimmedText = todo.trim();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (trimmedText) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;this&lt;/span&gt;.todos.unshift({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          id: nextTodoId++,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          text: trimmedText
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    removeTodo (idToRemove) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;this&lt;/span&gt;.todos = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;this&lt;/span&gt;.todos.filter(todo =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; todo.id !== idToRemove;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Edit &lt;code&gt;src/App.vue&lt;/code&gt; to include code that will render the Todo App.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;template&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;id&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;app&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;h1&lt;/span&gt;&amp;gt;My Todo App!&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;h1&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;TodoList&lt;/span&gt;/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;template&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; TodoList from &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;./components/TodoList.vue&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;default&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  components: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    TodoList
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;style&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;*, *::&lt;span style=&#34;color:#555&#34;&gt;before&lt;/span&gt;, *::&lt;span style=&#34;color:#555&#34;&gt;after&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;box-sizing&lt;/span&gt;: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;border-box&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;#&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;app&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;max-width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;400&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;margin&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;auto&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;line-height&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1.4&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;font-family&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Avenir&amp;#39;&lt;/span&gt;, Helvetica, Arial, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sans-serif&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080&#34;&gt;-webkit-&lt;/span&gt;font-smoothing: antialiased;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080&#34;&gt;-moz-&lt;/span&gt;osx-font-smoothing: grayscale;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;color&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;#00C185&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;h1&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;text-align&lt;/span&gt;: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;center&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;style&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Then, run:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm run serve&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and visit &lt;a href=&#34;http://localhost:8080&#34;&gt;http://localhost:8080&lt;/a&gt; to view your Todo App!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/12/how-to-use-cypress-for-ui-testing/todo-app.webp&#34; alt=&#34;Screenshot of the Todo App opened in browser. The browser is viewing localhost:8080, and on the page, the header reads &amp;ldquo;My Todo App!&amp;rdquo; wth a box underneath labeled &amp;ldquo;Add a new todo&amp;rdquo;, along with an &amp;ldquo;Add&amp;rdquo; button to the right. Below is green text reading &amp;ldquo;Nothing left in the list. Add a new todo in the input above&amp;rdquo;.&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;how-to-write-end-to-end-tests&#34;&gt;How to write end-to-end tests&lt;/h3&gt;
&lt;p&gt;End-to-end (E2E) testing is used to test an application flow from start to finish. Tests are designed to use the application the same way that a user would.&lt;/p&gt;
&lt;p&gt;In our example we are going to test the whole Todo app with the following test cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The user should see a message when the to-do list is empty.&lt;/li&gt;
&lt;li&gt;The user should be able to view a list of to-dos.&lt;/li&gt;
&lt;li&gt;The user should be able to add a new to-do.&lt;/li&gt;
&lt;li&gt;The user should be able to remove an existing to-do.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cypress is built on top of &lt;a href=&#34;https://docs.cypress.io/guides/references/bundled-libraries#Mocha&#34;&gt;Mocha&lt;/a&gt; and &lt;a href=&#34;https://docs.cypress.io/guides/references/bundled-libraries#Chai&#34;&gt;Chai&lt;/a&gt;. If you&amp;rsquo;re familiar with writing tests in JavaScript, then writing tests in Cypress will be a breeze.&lt;/p&gt;
&lt;p&gt;Visit the official &lt;a href=&#34;https://docs.cypress.io/guides/end-to-end-testing/writing-your-first-end-to-end-test#Write-your-first-test&#34;&gt;Cypress docs&lt;/a&gt; to learn more about how to start testing a new project in Cypress.&lt;/p&gt;
&lt;p&gt;Without futher ado lets start testing our Todo app.&lt;/p&gt;
&lt;p&gt;Inside the &lt;code&gt;cypress&lt;/code&gt; folder that was added during installation create a file, &lt;code&gt;cypress/e2e/todo.cy.js&lt;/code&gt;, and add the following tests.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Please visit the &lt;a href=&#34;https://docs.Cypress.io/guides/core-concepts/writing-and-organizing-tests#Folder-structure&#34;&gt;folder structure docs&lt;/a&gt; to learn more about how to organise tests in Cypress and understand the generated Cypress folder structure.&lt;/p&gt;&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;/* eslint-disable no-undef */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// describe() function is used to group tests
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;describe(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Todo tests&amp;#39;&lt;/span&gt;, () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  it(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;should display empty state message&amp;#39;&lt;/span&gt;, () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// cy.visit() used to visit a remote url
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// learn more about it here: https://docs.cypress.io/api/commands/visit#Syntax
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    cy.visit(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;http://localhost:8080&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// cy.get() command Gets one or more DOM elements by selector or alias
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// learn more about Cypress commands/api here: https://docs.cypress.io/api/table-of-contents
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    cy.get(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;.empty-state-message&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .contains(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Nothing left in the list. Add a new todo in the input above.&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .should(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;be.visible&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  it(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;should add todo&amp;#39;&lt;/span&gt;, () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    cy.visit(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;http://localhost:8080&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// add todo by typing in the input and pressing enter
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    cy.get(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;input[type=&amp;#34;text&amp;#34;]&amp;#39;&lt;/span&gt;).type(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;new todo{enter}&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// check if the todo is added
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    cy.get(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ul&amp;#39;&lt;/span&gt;).contains(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;new todo&amp;#39;&lt;/span&gt;).should(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;be.visible&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    cy.get(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ul&amp;#39;&lt;/span&gt;).find(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;li&amp;#39;&lt;/span&gt;).should(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;have.length&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// add another todo by typing in the input and pressing &amp;#39;add&amp;#39; button
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    cy.get(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;input[type=&amp;#34;text&amp;#34;]&amp;#39;&lt;/span&gt;).type(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;more todo&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    cy.get(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;input[type=submit]&amp;#39;&lt;/span&gt;).click();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// check if the todo is added
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    cy.get(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ul&amp;#39;&lt;/span&gt;).contains(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;more todo&amp;#39;&lt;/span&gt;).should(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;be.visible&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    cy.get(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ul&amp;#39;&lt;/span&gt;).find(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;li&amp;#39;&lt;/span&gt;).should(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;have.length&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  it(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;should delete todo&amp;#39;&lt;/span&gt;, () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    cy.visit(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;http://localhost:8080&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// delete the first todo
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    cy.get(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;:nth-child(1) &amp;gt; button&amp;#39;&lt;/span&gt;).click();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// check if the todo is deleted
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// cy.should() command is used to assert that the todo list has only one todo
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// learn more about Cypress assertions here: https://docs.cypress.io/guides/references/assertions
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    cy.get(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ul&amp;#39;&lt;/span&gt;).find(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;li&amp;#39;&lt;/span&gt;).should(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;have.length&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To make Vue available at &lt;code&gt;http://localhost:8080&lt;/code&gt;, run:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm run serve&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, to launch Cypress, run:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm run cypress:open&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;On the Cypress launcher select &amp;ldquo;End2End Tests&amp;rdquo; and click on the &lt;code&gt;todo.cy.js&lt;/code&gt; spec to run your tests.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/12/how-to-use-cypress-for-ui-testing/todo-app-end-to-end-tests.webp&#34; alt=&#34;Screenshot displaying Todo App End2End tests that have run successfully. A test browser in the sidebar is open to todo.cy.js, with a successful test to the right. There are green check marks next to &amp;ldquo;should display empty state message&amp;rdquo;, &amp;ldquo;should add todo&amp;rdquo;, and &amp;ldquo;should delete todo&amp;rdquo;. After the first is the test body in a code block reading &amp;ldquo;visit http://localhost:8080; get .empty-state-message; -contains Nothing left in the list. Add a new todo in the input above.; -assert expected &amp;lt;p.empty-state-message&amp;gt; to be visible&amp;rdquo;.&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;how-to-write-component-tests&#34;&gt;How to write component tests&lt;/h3&gt;
&lt;p&gt;Cypress Component Test Runner executes your component tests in the browser as a user would by simulating real interactions. Since it runs in the browser, you get to debug your components using your favourite developer tools.&lt;/p&gt;
&lt;p&gt;To demonstrate how to write component tests using Cypress let&amp;rsquo;s write tests for the following components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;BaseTextInput.vue&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TodoListItem.vue&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;component-tests-for-basetextinputvue&#34;&gt;Component tests for BaseTextInput.vue&lt;/h4&gt;
&lt;p&gt;Here we are going to assert the following;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Text input is rendered with &lt;code&gt;placeholder&lt;/code&gt; text &amp;ldquo;Add a new todo&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;When the &amp;ldquo;add&amp;rdquo; button is clicked a &lt;code&gt;newTodo&lt;/code&gt; event is emitted with a payload containing text that was typed in the text input.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;First create a spec, &lt;code&gt;src/components/BaseTextInput.cy.js&lt;/code&gt;, then add the following code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;/* eslint-disable no-undef */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; BaseTextInput from &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;./BaseTextInput.vue&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;describe(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;lt;BaseTextInput /&amp;gt;&amp;#39;&lt;/span&gt;, () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  it(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;renders base text input component&amp;#39;&lt;/span&gt;, () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Renders the component in DOM.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// cy.mount() is a custom command.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Learn more about cypress custom commands here:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// https://docs.cypress.io/api/commands/mount#Creating-a-New-cy-mount-Command
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    cy.mount(BaseTextInput);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Asserts that the text input is rendered with the correct placeholder
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    cy.get(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;input&amp;#39;&lt;/span&gt;).should(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;have.attr&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;placeholder&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Add a new todo&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// asserts that when the &amp;#39;add&amp;#39; button is clicked, an event is emitted
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// with the payload containing the value of the text input
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    cy.get(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;input[type=&amp;#34;text&amp;#34;]&amp;#39;&lt;/span&gt;).type(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;new todo&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    cy.get(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;input[type=submit]&amp;#39;&lt;/span&gt;).click().then(() =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#888&#34;&gt;// Cypress.vueWrapper provides access to the Vue Test Utils.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;      &lt;span style=&#34;color:#888&#34;&gt;// With this wrapper you can access any Vue Test Utils API.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;      &lt;span style=&#34;color:#888&#34;&gt;// Learn more about Vue Test Utils here: https://vue-test-utils.vuejs.org/
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;      &lt;span style=&#34;color:#888&#34;&gt;// e.g. cy.vueWrapper().emitted() returns all the events emitted by the BaseTextInput component
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;      cy.wrap(Cypress.vueWrapper.emitted()).should(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;have.property&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;newTodo&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      expect(Cypress.vueWrapper.emitted().newTodo[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;]).to.deep.equal([&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;new todo&amp;#39;&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To run the test, launch Cypress using &lt;code&gt;cypress:open&lt;/code&gt;. This time select &amp;ldquo;component testing&amp;rdquo;, then click on &lt;code&gt;BaseTextInput.cy.js&lt;/code&gt; to run tests.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/12/how-to-use-cypress-for-ui-testing/base-text-input-tests.webp&#34; alt=&#34;Screenshot displaying BaseTextInput component tests that have run successfully. Cypress is open to src/components/BaseTextInput.cy.js. In the test output sidebar, there is one green check mark, next to &amp;ldquo;renders base text input component&amp;rdquo;. It is followed by the test body in a code block, which reads: &amp;ldquo;-mount &amp;lt;BaseTextInput &amp;hellip; /&amp;gt;; get input; -assert expected [ &amp;lt;input.input&amp;gt;, 1 more&amp;hellip;] to have attribute placeholder with the value Add a new todo; get input[type=&amp;ldquo;text&amp;rdquo;]; -click; assert expected [ new todo ] to deeply equal [ new todo ]; wrap Object{25}; -assert expected { Object (DOMSubtreeModified, pointerover, &amp;hellip;) } to have property newTodo&amp;rdquo;.&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;component-tests-for-todolistitemvue&#34;&gt;Component tests for TodoListItem.vue&lt;/h4&gt;
&lt;p&gt;Here we are going to assert the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A Todo list item is rendered with correct text.&lt;/li&gt;
&lt;li&gt;When the &amp;ldquo;remove&amp;rdquo; button is clicked, a &amp;ldquo;remove&amp;rdquo; event is emitted with payload containing the ID of the Todo to be removed.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;First create a spec, &lt;code&gt;src/components/TodoListItem.cy.js&lt;/code&gt;, and add the following code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;/* eslint-disable no-undef */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; TodoListItem from &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;./TodoListItem.vue&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;describe(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;lt;TodoListItem /&amp;gt;&amp;#39;&lt;/span&gt;, () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  it(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;renders todo list item&amp;#39;&lt;/span&gt;, () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; text = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;new todo&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; id = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// mount the component with props
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// see: https://test-utils.vuejs.org/guide/
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    cy.mount(TodoListItem, {props: {todo: { id, text }}});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// asserts that the todo list item is rendered with the correct text
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    cy.get(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;li&amp;#39;&lt;/span&gt;).contains(text);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// asserts that when &amp;#39;x&amp;#39; button is clicked, &amp;#39;remove&amp;#39; event is emitted with the correct payload
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    cy.get(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;button&amp;#39;&lt;/span&gt;).click().then(() =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      cy.wrap(Cypress.vueWrapper.emitted()).should(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;have.property&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;remove&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      expect(Cypress.vueWrapper.emitted().remove[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;]).to.deep.equal([id]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To run the test, launch Cypress using &lt;code&gt;cypress:open&lt;/code&gt;. Select &amp;ldquo;component testing&amp;rdquo; again, then click on &lt;code&gt;TodoListItem.cy.js&lt;/code&gt; to run tests.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/12/how-to-use-cypress-for-ui-testing/todo-list-item-test.webp&#34; alt=&#34;Screenshot displaying TodoListItem component tests that have run successfully. The same cypress window is navigated to src/components/TodoListItem.cy.js. There is a green check mark next to &amp;ldquo;renders todo list item. The test body cody block reads &amp;ldquo;-mount &amp;lt;TodoListItem &amp;hellip; /&amp;gt;; get li; -contains new todo; get button; -click; assert expected [ 1 ] to deeply equal [ 1 ]; wrap Object{16}; -assert expected { Object (DOMSubtreeModified, pointerover, &amp;hellip;) } to have property remove&amp;rdquo;.&#34;&gt;&lt;/p&gt;
&lt;p&gt;The final source code with all the tests can be found in &lt;a href=&#34;https://github.com/Mloweedgar/todo-app&#34;&gt;my GitHub todo-app repo&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;next-steps&#34;&gt;Next steps&lt;/h3&gt;
&lt;p&gt;This article is meant to provide you with knowledge on how to set up and run tests using Cypress. However, you may need to further learn Cypress and its API to write better and efficient tests. I recommend you continue learning with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.cypress.io/guides/core-concepts/introduction-to-cypress&#34;&gt;Cypress core concepts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.cypress.io/guides/guides/network-requests&#34;&gt;Network requests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.cypress.io/guides/references/best-practices&#34;&gt;Cypress best practices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

      </content>
    </entry>
  
    <entry>
      <title>Migrating a Node.js app database from MongoDB to PostgreSQL</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2022/02/migrating-mongodb-to-postgresql/"/>
      <id>https://www.endpointdev.com/blog/2022/02/migrating-mongodb-to-postgresql/</id>
      <published>2022-02-25T00:00:00+00:00</published>
      <author>
        <name>Phineas Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2022/02/migrating-mongodb-to-postgresql/20220101_214022-sm.webp&#34; alt=&#34;Trees covered with a small amount of snow against a blue sky and some white puffy clouds&#34;&gt;&lt;/p&gt;
&lt;!-- Photo by Jon Jensen --&gt;
&lt;p&gt;Recently I worked on a web app designed for tracking, editing, and reporting on archaeological survey data. When I joined the project, it used React on the front-end, Node.js on the back-end, GraphQL as the API interface, and MongoDB as the database. For the most part, this architecture and choice of technology worked well, and I was quickly able to make meaningful contributions to the app both in the user interface and in the backend. However, after a while, things started to get difficult and we began to consider &lt;em&gt;why&lt;/em&gt; we were using MongoDB, and what might be better.&lt;/p&gt;
&lt;h3 id=&#34;mongodb-vs-postgresql&#34;&gt;MongoDB vs. PostgreSQL&lt;/h3&gt;
&lt;p&gt;First, why was MongoDB difficult to use in our case? The biggest difficulty was using their query language for certain aspects of our database. Part of the database involved representing artifacts found at a site using a specific type hierarchy defined by the state government. Representing that hierarchy within Mongo was difficult, and querying it even more so. It was hard to find &lt;em&gt;how&lt;/em&gt; to query a recursive structure using the MongoDB query language.&lt;/p&gt;
&lt;p&gt;As I worked on this, scouring the documentation and Q&amp;amp;A websites to find ways to query this data efficiently, I began to realize that it was a problem that could be easily solved with PostgreSQL&amp;rsquo;s &lt;a href=&#34;https://www.postgresql.org/docs/current/queries-with.html#QUERIES-WITH-RECURSIVE&#34;&gt;recursive queries&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now, a single recursive table isn&amp;rsquo;t really a good reason to completely switch the database used by an application, but it got us thinking: Why &lt;em&gt;were&lt;/em&gt; we using MongoDB? What benefits was it offering?&lt;/p&gt;
&lt;p&gt;I found a relevant article published on the official MongoDB website entitled &lt;a href=&#34;https://www.mongodb.com/compare/mongodb-postgresql&#34;&gt;Comparing MongoDB vs PostgreSQL&lt;/a&gt;. That article is generally very positive in its portrayal of PostgreSQL, so I was interested to hear what they had to say for themselves. Here are some of the main points in favor of MongoDB that the article discusses:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MongoDB is horizontally scalable by default:&lt;/strong&gt; This seems cool, but we were working on a new app that was not expected to need to scale much, and a single PostgreSQL instance has been shown to scale more than enough for our anticipated needs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MongoDB supports fancy new serverless and mobile paradigms:&lt;/strong&gt; Again, cool, but this was a fairly small-scale project using a pretty normal Linux/​Node.js stack. No need for serverless support.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MongoDB&amp;rsquo;s document structure makes it easy for developers to change the structure of data on the fly:&lt;/strong&gt; While this can certainly be nice, it can also bring its own issues. If data is expected to fit a certain structure and one developer changes that structure, this will likely cause problems for other developers whether or not there is a schema. Additionally, our project had a small team of 1-3 developers working on it and changing the schema as needed just wasn&amp;rsquo;t a problem.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The MongoDB vs. PostgreSQL article also mentions &amp;ldquo;resilience&amp;rdquo; as a strength of MongoDB, citing its ability to easily be replicated and broken into shards across datacenters and regions. However, research regarding the historical reliability of PostgreSQL and MongoDB showed that MongoDB&amp;rsquo;s fairly short history is littered with data loss problems. Anecdotally, a Google search for &amp;ldquo;MongoDB data loss&amp;rdquo; returns 1.26 million results (an average of 0.14 million results per year since its initial release) while &amp;ldquo;PostgreSQL data loss&amp;rdquo; returns 1.57 million (0.06 million per year since release). More specifically, there are fascinating in-depth reviews of MongoDB&amp;rsquo;s reliability, such as those conducted by Jepsen as recently as 2020 (see &lt;a href=&#34;https://aphyr.com/posts/284-call-me-maybe-mongodb&#34;&gt;#1&lt;/a&gt;, &lt;a href=&#34;https://aphyr.com/posts/322-jepsen-mongodb-stale-reads&#34;&gt;#2&lt;/a&gt;, &lt;a href=&#34;https://jepsen.io/analyses/mongodb-3-4-0-rc3&#34;&gt;#3&lt;/a&gt;, &lt;a href=&#34;https://jepsen.io/analyses/mongodb-3-6-4&#34;&gt;#4&lt;/a&gt;, &lt;a href=&#34;https://jepsen.io/analyses/mongodb-4.2.6&#34;&gt;#5&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;PostgreSQL hasn&amp;rsquo;t been perfect in the 25 years since its initial release, and MongoDB has improved a lot in recent years, but it&amp;rsquo;s clear that Postgres is the winner when it comes to data reliability.&lt;/p&gt;
&lt;p&gt;After reviewing all of these pros and cons of MongoDB, it became clear that it wasn&amp;rsquo;t really offering anything that we needed, and our data, which was strictly organized and relational by nature, would fit much better in a relational database. We decided that it would be better to switch to PostgreSQL.&lt;/p&gt;
&lt;h3 id=&#34;mongoose-and-sequelize&#34;&gt;Mongoose and Sequelize&lt;/h3&gt;
&lt;p&gt;Our database was only being used within the backend GraphQL server, using &lt;a href=&#34;https://mongoosejs.com/&#34;&gt;Mongoose&lt;/a&gt;, an object modeling library for Node.js. Mongoose uses a schema/​model system, where every MongoDB collection contains documents fitting a schema defined within Mongoose model definitions, for example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; mongoose from &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;mongoose&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; { Schema } = mongoose;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; blogPostSchema = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Schema({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  title: &lt;span style=&#34;color:#038&#34;&gt;String&lt;/span&gt;, &lt;span style=&#34;color:#888&#34;&gt;// String is shorthand for {type: String}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;  author: &lt;span style=&#34;color:#038&#34;&gt;String&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  body: &lt;span style=&#34;color:#038&#34;&gt;String&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  comments: [{ body: &lt;span style=&#34;color:#038&#34;&gt;String&lt;/span&gt;, date: &lt;span style=&#34;color:#038&#34;&gt;Date&lt;/span&gt; }],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  date: { type: &lt;span style=&#34;color:#038&#34;&gt;Date&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;default&lt;/span&gt;: &lt;span style=&#34;color:#038&#34;&gt;Date&lt;/span&gt;.now },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  hidden: &lt;span style=&#34;color:#038&#34;&gt;Boolean&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  meta: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    votes: &lt;span style=&#34;color:#038&#34;&gt;Number&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    favs: &lt;span style=&#34;color:#038&#34;&gt;Number&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; BlogPost = mongoose.model(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;BlogPost&amp;#34;&lt;/span&gt;, blogPostSchema);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once a model is defined it can easily be used to query and create documents:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; new_post = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; BlogPost({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  title: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;On the international epidemic of counterfeit at-home brogal treatments&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  author: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Ronald McClure&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  body: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;...&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; new_post.save();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; posts = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; BlogPost.find({ author: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Ronald McClure&amp;#34;&lt;/span&gt; });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Mongoose provides &lt;a href=&#34;https://mongoosejs.com/docs/api/model.html&#34;&gt;a lot of functions&lt;/a&gt; for querying and editing documents in a collection on the &lt;code&gt;Model&lt;/code&gt; class, as well as a rich &lt;a href=&#34;https://mongoosejs.com/docs/api/query.html&#34;&gt;Query class&lt;/a&gt; to make manual queries, among other things. Their &lt;a href=&#34;https://mongoosejs.com/docs/guide.html&#34;&gt;documentation&lt;/a&gt; provides helpful guides and a great API reference.&lt;/p&gt;
&lt;p&gt;Because we generally liked the Mongoose interface and didn&amp;rsquo;t want to have to change our existing code too drastically, we searched for a similar library that could work with PostgreSQL and settled upon &lt;a href=&#34;https://sequelize.org/&#34;&gt;Sequelize&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Sequelize has a very similar interface to Mongoose, where you start by defining tables (instead of &amp;ldquo;collections&amp;rdquo;) as models in JavaScript:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; { Sequelize, DataTypes } = require(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;sequelize&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; sequelize = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Sequelize(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;postgres://user:pass@example.com:5432/dbname&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; BlogPost = sequelize.define(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;BlogPost&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;// Model attributes are defined here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;  title: DataTypes.STRING,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  author: DataTypes.STRING,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  body: DataTypes.STRING,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;// This will need to be modeled differently
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#888&#34;&gt;// comments: [{ body: DataTypes.STRING, date: Date }],
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;  date: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    type: DataTypes.DATETIME,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    defaultValue: DataTypes.NOW
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  hidden: DataTypes.BOOLEAN,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  votes: DataTypes.INTEGER,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  favs: DataTypes.INTEGER
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;// Other model options go here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Object creation is similar
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; new_post = BlogPost.build({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  title: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;On the international epidemic of counterfeit at-home brogal treatments&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  author: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Ronald McClure&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  body: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;...&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; new_post.save();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that this model, while mimicking the Mongoose schema as much as possible, still requires some changes to fit the relational model. First, the &lt;code&gt;comments&lt;/code&gt; field, which was an array of documents in the Mongoose object will need to be represented in another table, which can then be linked via foreign key.&lt;/p&gt;
&lt;p&gt;While this seems annoying in some ways — creating a new table will require creating another Sequelize model, setting up the relations correctly, etc. — it makes a lot of sense for this example (a blog) and most other situations. We don&amp;rsquo;t just want blog posts to have associated comments, but we likely also want those comments to be tied to an author and to have their own IDs, which would allow lookup by author (for example so that on a user&amp;rsquo;s page they can see all comments they&amp;rsquo;ve made on all posts) and by individual comment (so it&amp;rsquo;s easier to make links directly to a single comment).&lt;/p&gt;
&lt;p&gt;So, rather than having comments be a subfield of a blog post, we want them to be defined by some kind of relation. In MongoDB this can be done in a few ways, most obviously by creating a &lt;code&gt;comments&lt;/code&gt; collection making the comments list contain &lt;a href=&#34;https://mongoosejs.com/docs/schematypes.html#objectids&#34;&gt;&lt;code&gt;ObjectIDs&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; mongoose = require(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;mongoose&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; { Schema } = mongoose;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; Comment = mongoose.model(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Comment&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Schema({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    body: &lt;span style=&#34;color:#038&#34;&gt;String&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    date: &lt;span style=&#34;color:#038&#34;&gt;Date&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; blogPostSchema = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Schema({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;  comments: [{ type: Schema.Types.ObjectId, ref: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Comment&amp;#34;&lt;/span&gt; }],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; BlogPost = mongoose.model(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;BlogPost&amp;#34;&lt;/span&gt;, blogPostSchema);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Mongoose also has a way to automatically fill in those referenced objects with the real thing as needed, using their &lt;a href=&#34;https://mongoosejs.com/docs/populate.html&#34;&gt;populate&lt;/a&gt; functionality:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BlogPost.findOne({ author: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Ronald McClure&amp;#34;&lt;/span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .populate(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;comments&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .exec(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; (err, post) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (err) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; handleError(err);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    post.comments.forEach((comment) =&amp;gt; console.log(comment));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With the call to &lt;code&gt;.populate(&#39;comments&#39;)&lt;/code&gt;, the comments array is filled with objects instead of just the object IDs. Sequelize has similar functionality, but uses the concept of &lt;a href=&#34;https://sequelize.org/v6/manual/assocs.html&#34;&gt;associations&lt;/a&gt; which model the relational structure of SQL more closely:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; { Sequelize, DataTypes } = require(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;sequelize&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; sequelize = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Sequelize(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;postgres://user:pass@example.com:5432/dbname&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; BlogPost = sequelize.define(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;BlogPost&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;// ... fields here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;}, {});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; Comment = sequelize.define(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Comment&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  body: DataTypes.TEXT,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  date: DataTypes.DATE,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}, {});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Comment.belongsTo(BlogPost;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BlogPost.hasMany(Comment);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These &lt;code&gt;belongsTo&lt;/code&gt; and &lt;code&gt;hasMany&lt;/code&gt; associations use foreign key/​primary key references on the models, and Sequelize also provides a similar interface for filling in the referenced data:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; post = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; BlogPost.findOne({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  where: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    author: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Ronald McClure&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  include: Comment,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Both libraries also have ways to lazy-load related objects, only fetching them as necessary. For MongoDB, this involves writing your own queries on referenced fields or using MongoDB&amp;rsquo;s &lt;a href=&#34;https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/&#34;&gt;&lt;code&gt;$lookup&lt;/code&gt;&lt;/a&gt; operator, while Sequelize allows custom queries using the foreign key fields, as well as a set of nice &lt;a href=&#34;https://sequelize.org/v6/manual/assocs.html#special-methods-mixins-added-to-instances&#34;&gt;methods to load associations&lt;/a&gt; on demand.&lt;/p&gt;
&lt;h3 id=&#34;modeling-and-migrating-data&#34;&gt;Modeling and Migrating Data&lt;/h3&gt;
&lt;p&gt;Having weighed for this situation the pros and cons of PostgreSQL and MongoDB as well as Mongoose and Sequelize, the question then comes: How do we actually move the data from one system to another? Unfortunately, as far as I know there is no one tool that can be used to migrate data between MongoDB and PostgreSQL. However, using a combination of &lt;a href=&#34;https://docs.mongodb.com/database-tools/mongoexport/&#34;&gt;&lt;code&gt;mongoexport&lt;/code&gt;&lt;/a&gt; and some basic SQL files, it was not too difficult to migrate data.&lt;/p&gt;
&lt;p&gt;The first step and perhaps one of the more difficult ones is to create an SQL schema that works for your data. If your MongoDB data is already in a normalized and relational structure, it might not be too hard, but if you have a lot of subdocuments and arrays that need to be expanded into rows, things might be more difficult. My recommendation for how to approach this is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Model your data in SQL. If you&amp;rsquo;re doing a migration from MongoDB, you likely have an idea of what the application needs are and how data should be organized.&lt;/li&gt;
&lt;li&gt;Test your model with some dummy data. This will probably be a bit of a back-and-forth iterative process as you iron out issues with relations, decide what kind of constraints will be necessary, etc.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;mongoexport&lt;/code&gt; to export your data as appropriate. For many collections, it may be easy to simply export it as CSV and use Postgres&amp;rsquo;s &lt;a href=&#34;https://www.postgresql.org/docs/current/sql-copy.html&#34;&gt;&lt;code&gt;COPY&lt;/code&gt;&lt;/a&gt; command to import it into the appropriate table. However, many things won&amp;rsquo;t be so simple and might require writing your own script that reads the exported CSV or JSON and writes it to the database, altering data as needed. This might be necessary for example to change MongoDB&amp;rsquo;s object IDs to match a datatype in PostgreSQL, such as an identity column (defined in &lt;a href=&#34;https://www.postgresql.org/docs/current/sql-createtable.html&#34;&gt;&lt;code&gt;CREATE TABLE&lt;/code&gt;&lt;/a&gt;), &lt;a href=&#34;https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-SERIAL&#34;&gt;&lt;code&gt;serial&lt;/code&gt;&lt;/a&gt;, or &lt;a href=&#34;https://www.postgresql.org/docs/current/datatype-uuid.html&#34;&gt;&lt;code&gt;UUID&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Use your set of import commands and scripts to import all of your data into a test database.&lt;/li&gt;
&lt;li&gt;Modify your application to use Sequelize instead of Mongoose.&lt;/li&gt;
&lt;li&gt;Find and update all cases in your code where a Mongoose model or instance was used and update it to use Sequelize instead.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;caveats-and-gotchas&#34;&gt;Caveats and Gotchas&lt;/h3&gt;
&lt;p&gt;One of the biggest problems we came across after our migration was the differences in query syntax between Mongoose and Sequelize. Compare the following queries:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Mongoose
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;post = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; BlogPost.findOne({ author: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Ronald McClure&amp;#39;&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Sequelize
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;post = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; BlogPost.findOne({ author: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Ronald McClure&amp;#39;&lt;/span&gt; });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;They look the same, they both run with no errors, and they both return the one post we created. But say we change which author we are querying for:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Mongoose
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;post = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; BlogPost.findOne({ author: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Darlene Roberts&amp;#39;&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Sequelize
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;post = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; BlogPost.findOne({ author: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Darlene Roberts&amp;#39;&lt;/span&gt; });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Mongoose&amp;rsquo;s query returns nothing, as we expect, but Sequelize&amp;hellip; returns the same post as the &amp;ldquo;Ronald McClure&amp;rdquo; query returned? Astute readers might notice what the issue is from earlier examples: Sequelize&amp;rsquo;s &lt;a href=&#34;https://sequelize.org/v6/class/src/model.js~Model.html#static-method-findAll&#34;&gt;find methods&lt;/a&gt; take an &lt;code&gt;options&lt;/code&gt; object which doesn&amp;rsquo;t use its fields as &lt;code&gt;WHERE&lt;/code&gt; parameters by default. The correct way to write this query with Sequelize is:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;post = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; BlogPost.findOne({ where: { author: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Darlene Roberts&amp;#34;&lt;/span&gt; } });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It&amp;rsquo;s a bit of a silly mistake, but one that caused pain several times in the process of migration. Some queries seemed to work just fine, but just wouldn&amp;rsquo;t return the right results!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Building a search suggestions feature with Node.js and Vue</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2021/11/search-suggestions-with-node-and-vue/"/>
      <id>https://www.endpointdev.com/blog/2021/11/search-suggestions-with-node-and-vue/</id>
      <published>2021-11-27T00:00:00+00:00</published>
      <author>
        <name>Greg Davidson</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2021/11/search-suggestions-with-node-and-vue/banner.jpg&#34; alt=&#34;Old Dog&#34;&gt;
&lt;a href=&#34;https://unsplash.com/photos/L1jHI4ThA44&#34;&gt;Photo&lt;/a&gt; by &lt;a href=&#34;https://unsplash.com/@kaspercph&#34;&gt;Kasper Rasmussen&lt;/a&gt; on Unsplash&lt;/p&gt;
&lt;h3 id=&#34;the-backstory&#34;&gt;The backstory&lt;/h3&gt;
&lt;p&gt;Some time ago, I worked on a project to improve the usability of a search component for our clients. Similar to Google and other search interfaces, the user was presented with a number of suggested search terms as they typed into the search box. We wanted to add keyboard support and give the component a visual facelift. When the customer used the up, down, &lt;kbd&gt;Esc&lt;/kbd&gt;, or &lt;kbd&gt;Enter&lt;/kbd&gt; or &lt;kbd&gt;Return&lt;/kbd&gt; keys, the component would allow them to choose a particular search term, clear their search, or navigate to the results for their chosen search term.&lt;/p&gt;
&lt;p&gt;This is what the new and improved UI looked like:
&lt;img src=&#34;/blog/2021/11/search-suggestions-with-node-and-vue/search-suggestions-ui.jpg&#34; alt=&#34;Search interface with suggested search terms&#34;&gt;&lt;/p&gt;
&lt;p&gt;As developers, it can sometimes feel like we&amp;rsquo;re stuck when working on older, well established projects. We gaze longingly at newer and shinier tools. Part of my objective while building this feature was to prove the viability of a newer approach (Node.js and Vue) to the other engineers on the project as well as the client.&lt;/p&gt;
&lt;p&gt;The feature existed already but we wanted to improve the UX and performance. Having added several Vue-powered features to this site in the past, I was very comfortable with the idea and have written about that &lt;a href=&#34;/blog/2017/12/enhancing-your-sites-with-vue/&#34;&gt;previously&lt;/a&gt;. It would also be very easy to roll back if needed since this project was limited in scope, and very easy to compare the new solution with the code it replaced.&lt;/p&gt;
&lt;h3 id=&#34;picking-a-route-and-configuring-it&#34;&gt;Picking a route and configuring it&lt;/h3&gt;
&lt;p&gt;This project was running on &lt;a href=&#34;https://www.interchangecommerce.org/i/dev&#34;&gt;Interchange&lt;/a&gt;, nginx, and MySQL, but our approach would work in other stacks (e.g. with Apache). One key concept is that we&amp;rsquo;re using nginx as a reverse proxy to route requests to our various apps, serve static files, etc. Using nginx in this way allows us to stitch together the different services for a given project. In this case I created an endpoint for the search suggestions endpoint in our nginx config file. This enabled requests made to &lt;code&gt;/suggestions/&lt;/code&gt; to be passed along to our Node.js app. You can read more about &lt;a href=&#34;https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/&#34;&gt;reverse proxying with nginx&lt;/a&gt; if you like.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-nginx&#34; data-lang=&#34;nginx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# Node.js powered endpoint for search suggestions
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;location&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/suggestions/&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;proxy_pass&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;http://0.0.0.0:8741/&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If your project is running on Apache or another platform there will likely be a similar configuration option to route the requests to your Node.js app as I did.&lt;/p&gt;
&lt;h3 id=&#34;nodejs-time&#34;&gt;Node.js time&lt;/h3&gt;
&lt;p&gt;I wrote a little app with the &lt;a href=&#34;https://www.npmjs.com/package/mysql&#34;&gt;MySQL driver for Node.js&lt;/a&gt; and &lt;a href=&#34;https://expressjs.com/&#34;&gt;Express&lt;/a&gt;. The app connects to MySQL and uses the nifty built-in connection pooling. It accepts POST requests from the site and responds with an array of search suggestion objects in JSON format.&lt;/p&gt;
&lt;h3 id=&#34;enter-vue&#34;&gt;Enter Vue&lt;/h3&gt;
&lt;p&gt;For the front-end part of the feature I created Vue components for the search input and for the display of the results. As the customer types, results are fetched and displayed. Using the arrow keys navigates up and down and &lt;kbd&gt;Esc&lt;/kbd&gt; clears out the search. Once the customer has the search they want they can either press &lt;kbd&gt;Enter&lt;/kbd&gt; or click on the Search button.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s worth mentioning that this search feature works without JavaScript. When you enter a search term and press &lt;kbd&gt;Enter&lt;/kbd&gt; or click the Search button, you will get results. However, if you do have JavaScript enabled (and if our suggestions app is up and running) you&amp;rsquo;ll get suggestions as you type. This is a good thing.&lt;/p&gt;
&lt;h3 id=&#34;performance-wins&#34;&gt;Performance wins&lt;/h3&gt;
&lt;p&gt;The new endpoint returns results in less than 100ms — a 3x or 4x improvement over the Perl script it replaced. We get a response faster, meaning the experience is much more smooth for the user!&lt;/p&gt;
&lt;h3 id=&#34;managing-the-nodejs-process&#34;&gt;Managing the Node.js process&lt;/h3&gt;
&lt;p&gt;We used &lt;a href=&#34;https://pm2.keymetrics.io/docs/usage/pm2-doc-single-page/&#34;&gt;pm2&lt;/a&gt; and &lt;a href=&#34;https://systemd.io/&#34;&gt;systemd&lt;/a&gt; to manage the Node.js processes and ensure they are started up when the server is rebooted. In my experience this has been very stable and has not required any babysitting by our operations folks.&lt;/p&gt;
&lt;h3 id=&#34;great-success&#34;&gt;Great success!&lt;/h3&gt;
&lt;p&gt;Have you done anything similar in your projects? Do you have vintage/​legacy app that could benefit from learning some new tricks? Let us know!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>An Introduction to TypeScript</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2021/02/intro-to-typescript/"/>
      <id>https://www.endpointdev.com/blog/2021/02/intro-to-typescript/</id>
      <published>2021-02-09T00:00:00+00:00</published>
      <author>
        <name>Jeff Laughlin</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2021/02/intro-to-typescript/ts-lettermark-white.svg&#34; alt=&#34;TypeScript logo&#34;&gt;&lt;/p&gt;
&lt;p&gt;TypeScript is a programming language defined as a superset of JavaScript. It adds static type information to JavaScript code using type annotations. These annotations permit strong type-checking at compile-time, kind of like a very strict linter. They are only used for static analysis. TypeScript is transpiled to JavaScript for execution in the browser or Node.js and the type annotations are stripped out. It is still possible to use standard JavaScript type information at run-time, such as that obtained using the &lt;code&gt;typeof&lt;/code&gt; and &lt;code&gt;instanceof&lt;/code&gt; operators.&lt;/p&gt;
&lt;p&gt;Adding type annotations brings many benefits. Most importantly, TypeScript can tell us when we’re doing something dumb that’s likely to cause a type-related bug. But more than that, it powers editors’/​IDEs’ context sensitive tool tips so when you hover or start typing the editor can supply helpful information so you can get your job done quicker. This is particularly useful to new developers as it saves them the trouble of reading all the sources to figure out the variable types from context, especially when debugging.&lt;/p&gt;
&lt;p&gt;JavaScript is a fairly dynamic language but types still exist in JavaScript, whether we like it or not. Because it’s so dynamic it assumes that you the programmer know the type of every object you are using on every line of code and will do nothing to help you get it right. The type is specified by the context. The trouble is that in any non-trivial codebase it becomes impossible to be absolutely 100% certain about the type of some particular object that’s being passed around without reading the context, which is often a ton of code.&lt;/p&gt;
&lt;p&gt;By eagerly annotating our JavaScript code with types we can eliminate entire classes of bugs from our codebase. TypeScript will never let you play loosey-goosey with integers and strings, for example. If you want a variable to be able to hold an int or a string you must explicitly declare it as type “integer” or “string”.&lt;/p&gt;
&lt;p&gt;TypeScript lowers the total cost of ownership of a codebase by saving time. Programmers no longer have to guess or infer types and spend less time reading documentation and fixing bugs. Because fewer classes of errors are possible, less testing is required. Entire classes of serious problems never make it to the users because developers never commit them to the codebase. New developers on-board faster and make fewer mistakes. When multiple developers collaborate, their intentions with respect to types are immediately known to all, including the TS compiler, without reading any docs.&lt;/p&gt;
&lt;p&gt;TypeScript is open source software maintained by Microsoft: It is free to use, and the specification and tools are provided under the Apache open source license. Microsoft has cultivated a strong developer community with high levels of engagement.&lt;/p&gt;
&lt;h3 id=&#34;strictness&#34;&gt;Strictness&lt;/h3&gt;
&lt;p&gt;Note that TypeScript has several settings for controlling the strictness of its type checking. To obtain maximum benefit we recommend using the &lt;code&gt;--strict&lt;/code&gt; option which turns on all strict-checking options. This blog post assumes that strict mode is enabled. For more information see the &lt;a href=&#34;https://www.typescriptlang.org/docs/handbook/compiler-options.html&#34;&gt;TypeScript Compiler Options&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;declaring-variables-and-functions&#34;&gt;Declaring Variables and Functions&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; someBool: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;boolean&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;TypeScript will complain if you try to do this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;someBool = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;hello world!&amp;#34;&lt;/span&gt;;  &lt;span style=&#34;color:#888&#34;&gt;// this is a TypeScript error; someBool
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;                            &lt;span style=&#34;color:#888&#34;&gt;// cannot hold a string.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Another way to declare types is to assign directly to constants:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; someBool = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;TypeScript knows through inference that someBool is of type &lt;code&gt;boolean&lt;/code&gt; because it’s a const assigned from a boolean type expression. Later TypeScript will complain if you try to assign a non-boolean typed value to someBool.&lt;/p&gt;
&lt;h3 id=&#34;functions&#34;&gt;Functions&lt;/h3&gt;
&lt;p&gt;Function declaration is similar to variable declaration:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; someFunc(someBool: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;boolean&lt;/span&gt;): string {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Hello, world!&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This useless function takes one boolean type argument and returns a string type value:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; someFunc = (someBool: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;boolean&lt;/span&gt;): string =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Hello, world!&amp;#34;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;TypeScript requires parentheses around annotated lambda arguments, even for a single argument.&lt;/p&gt;
&lt;h3 id=&#34;assertions&#34;&gt;Assertions&lt;/h3&gt;
&lt;p&gt;TypeScript supports “type assertions”, which is a way of telling the compiler that you are very certain a particular value is of a particular type and that it should assume you are right. These are sometimes called “casts” in other languages.&lt;/p&gt;
&lt;p&gt;Generally speaking, type assertions are a red flag. There’s usually a better, more strongly-type way to write the code that avoids their usage. But sometimes they are convenient, particularly when interfacing with untyped JavaScript APIs. Of course it would be better to declare types for those APIs, but this is beyond the scope of some projects.&lt;/p&gt;
&lt;p&gt;Type assertions use the &lt;code&gt;as&lt;/code&gt; keyword:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; someBool: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;boolean&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;someBool as unknown as string = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;hello world&amp;#34;&lt;/span&gt;;  &lt;span style=&#34;color:#888&#34;&gt;// Not an error (but probably a mistake)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Because this is case is so egregious, and likely to be a programmer error, TS forces you to double cast it. A single &lt;code&gt;as&lt;/code&gt; is usually sufficient.&lt;/p&gt;
&lt;h3 id=&#34;interfaces&#34;&gt;Interfaces&lt;/h3&gt;
&lt;p&gt;Ok, now the real fun begins. So far we’ve only looked at primitive types like string and boolean. What about objects, arrays, and all that?&lt;/p&gt;
&lt;p&gt;TypeScript “interfaces” allow us to declare the properties and behaviors that objects should have. There are required properties, optional properties, etc.&lt;/p&gt;
&lt;p&gt;Interface declaration looks somewhat like object usage:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;interface&lt;/span&gt; myInterface {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  someBool: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;boolean&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  someString: string;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now when we use it, both someBool and someString are required. Hence, the following attempt to assign an empty object to a const of type myInterface is a TypeScript error:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; myObj: myInterface = {};  &lt;span style=&#34;color:#888&#34;&gt;// TypeScript error!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The object we assign must have both required properties:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; myObj: myInterface = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  someBool: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  someString: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Hello&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};  &lt;span style=&#34;color:#888&#34;&gt;// This works.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Optional fields are annotated using the ? character:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;interface&lt;/span&gt; myInterface {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  thisIsRequired: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;boolean&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  thisIsOptional?: string;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; myObj: myInterface = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  thisIsRequired: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;union-types&#34;&gt;Union Types&lt;/h3&gt;
&lt;p&gt;Sometimes you want to say that “this thing is of this type&amp;hellip; &lt;em&gt;or&lt;/em&gt; that type.” Enter “union types”:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;type boolOrString = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;boolean&lt;/span&gt; | string;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; myVar: boolOrString;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;myVar = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;myVar = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;hello!&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is 100% valid. myVar can hold either a boolean &lt;em&gt;or&lt;/em&gt; a string value. However, TypeScript will force you to disambiguate which type it currently holds before you can do anything unsafe with it. For example, if we have a function that takes a union type:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; upperBool(arg: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;boolean&lt;/span&gt; | string): string {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;typeof&lt;/span&gt; arg == &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;string&amp;#39;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; arg.toUpperCase();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;typeof&lt;/span&gt; arg == &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;boolean&amp;#39;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; arg.toString().toUpperCase();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It looks like our function could fall through and return undefined, and theoretically in JavaScript it could, but any such usage would trigger an error in TypeScript (assuming you are using &lt;code&gt;--strictNullChecks&lt;/code&gt;, part of the &lt;code&gt;--strict&lt;/code&gt; group of checks we recommend). TypeScript “knows” that we’ve handled both possible types of arg and so the if/else definitely does not fall through.&lt;/p&gt;
&lt;h3 id=&#34;intersection-types&#34;&gt;Intersection Types&lt;/h3&gt;
&lt;p&gt;An intersection type combines two or more interfaces into a single compound interface:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;interface&lt;/span&gt; This {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  aProperty: string;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;interface&lt;/span&gt; That {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  bProperty: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;boolean&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;type Both = This &amp;amp; That;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; both: Both = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  aProperty: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;funny&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  bProperty: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;classes&#34;&gt;Classes&lt;/h3&gt;
&lt;p&gt;TS has classes. I won’t get deep into TS classes, but I want to mention them. They can transpile to ES6 classes or ES2015 prototypal code. They are very powerful. They are, however, optional, as classes are still somewhat “new” to many JavaScript developers.&lt;/p&gt;
&lt;h3 id=&#34;enums&#34;&gt;Enums&lt;/h3&gt;
&lt;p&gt;TypeScript supports enum declarations. An enum is a type that can only hold one of a specific set of possible values.&lt;/p&gt;
&lt;h3 id=&#34;generics&#34;&gt;Generics&lt;/h3&gt;
&lt;p&gt;Generics are a mechanism for declaring “complex” types like arrays and promises. Creating new generics is somewhat advanced, but a basic understanding is required to use TS effectively.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; arrayOfStrings: string[];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; anotherArrayOfStrings: &lt;span style=&#34;color:#038&#34;&gt;Array&lt;/span&gt;&amp;lt;string&amp;gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Both of these syntaxes are equivalent. They both declare “an array of strings”.&lt;/p&gt;
&lt;p&gt;TypeScript includes a significant number of built-in generic types and you will need them to use the language effectively.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; promiseMeAString: &lt;span style=&#34;color:#038&#34;&gt;Promise&lt;/span&gt;&amp;lt;string&amp;gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This declares a promise that is expected to resolve to a string type value.&lt;/p&gt;
&lt;h3 id=&#34;indexable-types&#34;&gt;Indexable Types&lt;/h3&gt;
&lt;p&gt;Indexable types allow the developer to specify the key and value types expected when using the square-bracket operator &lt;code&gt;[]&lt;/code&gt; to access object or array properties. This is appropriate when the key set is large or arbitrary and not known in advance. In this case it’s not possible to define each possible key value.&lt;/p&gt;
&lt;p&gt;This interface describes an object that holds a collection of string type keys with boolean values:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;interface&lt;/span&gt; stringKeysBoolVals {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [key: string]: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;boolean&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The syntax is a little funny in that it includes a “placeholder” field, which I’ve set to &lt;code&gt;key&lt;/code&gt;. This field is essentially a comment. TS ignores it. You can set it to anything that makes sense, e.g. &lt;code&gt;index&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This constructs an object acting as a collection of arbitrary key/​value pairs using the interface we just declared:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; myCollection: stringKeysBoolVals = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;value1&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;value2&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;value3&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This usage is invalid because neither the key type of int nor the value-type of string is compatible with the declared interface:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; invalidCollection: stringKeysBoolVals = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;foobar&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;a-note-about-tooling&#34;&gt;A Note about Tooling&lt;/h3&gt;
&lt;p&gt;To get the most out of TypeScript it’s critical that your editor integrates with it and supports real-time type checking and error highlighting. Yes, you can run the TS compiler and check for errors at the command line, but this is a much slower development process.&lt;/p&gt;
&lt;p&gt;Also, most editors will be able to use TypeScript annotations to provide context-sensitive help and tell you about types and declarations, which is really helpful. Modern APIs are too complicated to memorize; don’t try.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;I hope this helps bring to light some of the wonderful features of TypeScript that help us write more reliable, more maintainable, and more readable code.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://www.typescriptlang.org/docs&#34;&gt;official TypeScript website&lt;/a&gt; is a great place to learn more.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Testing to defend against nginx add_header surprises</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/05/nginx-add_header-surprises-testing/"/>
      <id>https://www.endpointdev.com/blog/2020/05/nginx-add_header-surprises-testing/</id>
      <published>2020-05-29T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;img src=&#34;/blog/2020/05/nginx-add_header-surprises-testing/20200408-104315-mod.jpg&#34; alt=&#34;Cute calico cat perched securely upon a trepidatious shoe&#34; /&gt;
&lt;!-- Photo by Jon Jensen --&gt;
&lt;p&gt;These days when hosting websites it is common to configure the web server to send several HTTP response headers with every single request for security purposes.&lt;/p&gt;
&lt;p&gt;For example, using the nginx web server we may add these directives to our &lt;code&gt;http&lt;/code&gt; configuration scope to apply to everything served, or to specific &lt;code&gt;server&lt;/code&gt; configuration scopes to apply only to particular websites we serve:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;add_header Strict-Transport-Security max-age=2592000 always;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;add_header X-Content-Type-Options    nosniff         always;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(See &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security&#34;&gt;HTTP Strict Transport Security&lt;/a&gt; and &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options&#34;&gt;X-Content-Type-Options&lt;/a&gt; at MDN for details about these two particular headers.)&lt;/p&gt;
&lt;h3 id=&#34;the-surprise-problem&#34;&gt;The surprise (problem)&lt;/h3&gt;
&lt;p&gt;Once upon a time I ran into a case where nginx usually added the expected HTTP response headers, but later appeared to be inconsistent and sometimes did not. This is distressing!&lt;/p&gt;
&lt;p&gt;Troubleshooting leads to the (re-)discovery that &lt;code&gt;add_header&lt;/code&gt; directives are not always additive throughout the configuration as one would expect, and as every other server I can think of typically does.&lt;/p&gt;
&lt;p&gt;If you define your &lt;code&gt;add_header&lt;/code&gt; directives in the &lt;code&gt;http&lt;/code&gt; block and then use an &lt;code&gt;add_header&lt;/code&gt; directive in a &lt;code&gt;server&lt;/code&gt; block, those from the &lt;code&gt;http&lt;/code&gt; block will disappear.&lt;/p&gt;
&lt;p&gt;If you define some &lt;code&gt;add_header&lt;/code&gt; directives in the &lt;code&gt;server&lt;/code&gt; block and then add another &lt;code&gt;add_header&lt;/code&gt; directive in a &lt;code&gt;location&lt;/code&gt; block, those from the &lt;code&gt;http&lt;/code&gt; and/or &lt;code&gt;server&lt;/code&gt; blocks will disappear.&lt;/p&gt;
&lt;p&gt;This is even the case in an &lt;code&gt;if&lt;/code&gt; block.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&#34;https://nginx.org/en/docs/http/ngx_http_headers_module.html#add_header&#34;&gt;nginx &lt;code&gt;add_header&lt;/code&gt; documentation&lt;/a&gt; we find the reason for the behavior explained:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There could be several add_header directives. These directives are inherited from the previous level if and only if there are no add_header directives defined on the current level.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;This nginx directive has always behaved this way. Various people have warned about it in blog posts and online discussions for many years. But the situation remains the same, a trap for the unwary.&lt;/p&gt;
&lt;p&gt;I have tried to imagine the rationale behind this behavior. Response headers often are set in groups, so the programmer who created this feature may have decided that any new scope’s &lt;code&gt;add_header&lt;/code&gt; directives should start with a clean slate, unaffected by those set elsewhere. Hmm. The need for exclusive grouping of response headers is rare in my experience, and adding headers to the existing stack of tentative response headers is far more commonly what I want.&lt;/p&gt;
&lt;p&gt;So while this behavior may make sense somewhere, it has not ever done so for me or anyone I have talked to about it. For us it is simply misbehavior, silent and easy to overlook when making later seemingly unrelated configuration adjustments.&lt;/p&gt;
&lt;h3 id=&#34;dangers&#34;&gt;Dangers&lt;/h3&gt;
&lt;p&gt;It often has security implications when headers you thought were being added to every response are not. Consider more fine-tuned and consequential security-related headers such as &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy&#34;&gt;&lt;code&gt;Content-Security-Policy&lt;/code&gt;&lt;/a&gt;, &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Vary&#34;&gt;&lt;code&gt;Vary&lt;/code&gt;&lt;/a&gt; for cache object separation, &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS&#34;&gt;CORS&lt;/a&gt; headers &lt;code&gt;Access-Control-*&lt;/code&gt;, etc.&lt;/p&gt;
&lt;p&gt;Headers such as these are especially important when they need to be added based on logic spread across various configuration blocks, and that is exactly when nginx &lt;code&gt;add_headers&lt;/code&gt; doesn’t work as expected.&lt;/p&gt;
&lt;p&gt;Another pitfall is omitting the &lt;code&gt;always&lt;/code&gt; option to &lt;code&gt;add_header&lt;/code&gt;. Without that, the header will only be added to success responses (2XX and 3XX, but see the docs for specifics). We usually want security-related headers to be added even to 4XX and 5XX error responses.&lt;/p&gt;
&lt;h3 id=&#34;workaround-using-include&#34;&gt;Workaround using include&lt;/h3&gt;
&lt;p&gt;My first instinct was to work around the problems caused by this behavior by putting the standard add_header list in a file that I include everywhere. In some cases that works.&lt;/p&gt;
&lt;p&gt;But despite the &lt;a href=&#34;https://nginx.org/en/docs/ngx_core_module.html#include&#34;&gt;nginx include documentation&lt;/a&gt; saying that directive is allowed in “Context: any”, &lt;code&gt;include&lt;/code&gt; is &lt;em&gt;not&lt;/em&gt; allowed in an &lt;code&gt;if&lt;/code&gt; block and will result in the fatal startup error:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;include&amp;rdquo; directive is not allowed here&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;So the only recourse in those cases is to repeat all needed &lt;code&gt;add_header&lt;/code&gt; directives in every &lt;code&gt;if&lt;/code&gt; block that uses &lt;code&gt;add_header&lt;/code&gt;. Gross.&lt;/p&gt;
&lt;p&gt;Repeating configuration manually means almost surely having the &lt;code&gt;add_header&lt;/code&gt; directives in different configuration areas drift over time. So if we have to repeat ourselves, at least let’s do it with automation, such as by using configuration templating and preprocessing.&lt;/p&gt;
&lt;p&gt;That is what I have most recently done. And we can still use native nginx &lt;code&gt;include&lt;/code&gt; directives everywhere those are allowed.&lt;/p&gt;
&lt;h3 id=&#34;nginx-headers-more-module&#34;&gt;nginx Headers More module&lt;/h3&gt;
&lt;p&gt;Many people have run into exactly this problem, and some of them developed a separate nginx module &lt;a href=&#34;https://github.com/openresty/headers-more-nginx-module#readme&#34;&gt;ngx_headers_more&lt;/a&gt; to solve most of these problems.&lt;/p&gt;
&lt;p&gt;By using its &lt;code&gt;more_set_headers&lt;/code&gt; directive, you get the expected additive behavior with previously-declared headers, regardless of the block scope:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Directives inherited from an upper level scope (say, http block or server blocks) are executed before the directives in the location block.&lt;/p&gt;
&lt;p&gt;Note that although more_set_headers is allowed in location if blocks, it is not allowed in the server if blocks …&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Fortunately I have not needed to use this in an &lt;code&gt;if&lt;/code&gt; block in the &lt;code&gt;server&lt;/code&gt; scope, so that one remaining limitation doesn’t pose a problem for me.&lt;/p&gt;
&lt;p&gt;It also has options to set a header only for responses of a certain HTTP content type or status code.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;more_clear_headers&lt;/code&gt; directive allows the &lt;code&gt;*&lt;/code&gt; wildcard for clearing all headers with the same prefix at once, such as &lt;code&gt;Access-Control-*&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&#34;installing-ngx_headers_more&#34;&gt;Installing ngx_headers_more&lt;/h4&gt;
&lt;p&gt;Because “Headers More” is a separate module, not part of standard nginx, it is not usually available without some extra work.&lt;/p&gt;
&lt;p&gt;You can build it from source and install it manually, but of course that isn’t good to do on a production machine since it won’t get updated on its own.&lt;/p&gt;
&lt;p&gt;You can use the &lt;a href=&#34;https://openresty.org/en/&#34;&gt;OpenResty&lt;/a&gt; server built around nginx, which “Headers More” is part of. But you may not want all of that if you’re not writing a Lua web application.&lt;/p&gt;
&lt;p&gt;Many Linux distributions and 3rd-party package repositories have prebuilt packages for “Headers More” which you can use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Alpine
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;nginx-mod-http-headers-more&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Debian &amp;amp; Ubuntu
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;nginx-extras&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;libnginx-mod-http-headers-more-filter&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;RHEL/CentOS
&lt;ul&gt;
&lt;li&gt;GetPageSpeed &amp;amp; Webtatic repos &lt;code&gt;nginx-module-headers-more&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Aeris repo &lt;code&gt;nginx-more&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Search the excellent &lt;a href=&#34;https://pkgs.org/&#34;&gt;pkgs.org&lt;/a&gt; to find what you need if it isn’t already available through your package manager.&lt;/p&gt;
&lt;h3 id=&#34;apache&#34;&gt;Apache&lt;/h3&gt;
&lt;p&gt;Apache httpd is still alive and well — actually better than ever. So depending on your situation, you may want to use that instead.&lt;/p&gt;
&lt;p&gt;Apache’s &lt;a href=&#34;https://httpd.apache.org/docs/2.4/mod/mod_headers.html#header&#34;&gt;Header directive&lt;/a&gt; has intuitive (to me) default behavior for setting response headers across the whole configuration, and many ways to deal with a possibly already-existing header:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;add another header, or set exclusively (replace), or set only if this header doesn’t already exist&lt;/li&gt;
&lt;li&gt;append to or merge into an existing header (for headers that accept multiple values)&lt;/li&gt;
&lt;li&gt;edit an existing header with a regular expression search-and-replace&lt;/li&gt;
&lt;li&gt;unset a header if one was previously set&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I don’t know a way to have Apache clear a group of headers with a wildcard, or all headers at once, so they need to be individually cleared by name if that’s what you want.&lt;/p&gt;
&lt;h3 id=&#34;доверяй-но-проверяй-trust-but-verify&#34;&gt;Доверяй, но проверяй (Trust, but verify)&lt;/h3&gt;
&lt;p&gt;nginx was written by Igor Sysoev. Despite my disagreement with this one feature’s behavior, overall I find that nginx is excellent. Because of its open source release, excellent performance, and wide use, it has provided much-needed competition to Apache and Microsoft IIS. Thank you, Igor and all other contributors!&lt;/p&gt;
&lt;p&gt;In the relevant spirit, since Igor is Russian, I close with the Russian proverb Доверяй, но проверяй: Trust, but verify.&lt;/p&gt;
&lt;p&gt;Let us code (and configure) defensively, yet also test to avoid being surprised by missing headers.&lt;/p&gt;
&lt;p&gt;We can manually test various HTTP responses are as we expect using &lt;code&gt;curl -v&lt;/code&gt; or other HTTP clients to exercise various requests.&lt;/p&gt;
&lt;p&gt;Even better, we can add to our automated test suite to confirm these HTTP response headers appear everywhere we expect, for static files and API endpoints backed by different application servers, and for various success and error responses.&lt;/p&gt;
&lt;p&gt;Here is a test adapted from one I put together for one of our clients. It uses JavaScript in &lt;a href=&#34;https://nodejs.org/en/&#34;&gt;Node.js&lt;/a&gt;, the &lt;a href=&#34;https://jestjs.io/&#34;&gt;Jest&lt;/a&gt; test framework, and the &lt;a href=&#34;https://github.com/axios/axios&#34;&gt;Axios&lt;/a&gt; HTTP client. It ensures the security headers example I showed at the beginning of this article keeps working, even as we make nginx configuration changes over time:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; axios = require(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;axios&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; http = axios.create({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  baseURL: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;https://your.dom.ain&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;describe(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Check security headers&amp;#39;&lt;/span&gt;, () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; verifs = [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { header: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;strict-transport-security&amp;#39;&lt;/span&gt;, expect: (x) =&amp;gt; x.toMatch(&lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/max-age=\d{3,}/&lt;/span&gt;) },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { header: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;x-content-type-options&amp;#39;&lt;/span&gt;,    expect: (x) =&amp;gt; x.toEqual(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;nosniff&amp;#39;&lt;/span&gt;)        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; locs = [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { path: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/robots.txt&amp;#39;&lt;/span&gt;,                status: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;200&lt;/span&gt; },  &lt;span style=&#34;color:#888&#34;&gt;// static
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    { path: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/feed/endpoint/of/interest&amp;#39;&lt;/span&gt;, status: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;200&lt;/span&gt; },  &lt;span style=&#34;color:#888&#34;&gt;// API backend in PHP
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    { path: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/api/other/auth/endpoint&amp;#39;&lt;/span&gt;,   status: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;403&lt;/span&gt; },  &lt;span style=&#34;color:#888&#34;&gt;// API backend in Perl
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    { path: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/never/gonna/give/you/up!&amp;#39;&lt;/span&gt;,  status: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;404&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { path: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/api/dies/for/testing&amp;#39;&lt;/span&gt;,      status: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;500&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;// throw no exceptions for non-success HTTP response status
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; conf = { validateStatus: () =&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; l &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;of&lt;/span&gt; locs) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    test(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${&lt;/span&gt;l.status&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${&lt;/span&gt;l.path&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;async&lt;/span&gt; () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; res = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; http.get(l.path, conf);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      expect(res.status).toBe(l.status);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; v &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;of&lt;/span&gt; verifs) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        v.expect(expect(res.headers[v.header]));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here I run just this one test rather than the whole suite:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;% jest -w 6 ./__tests__/webserver/security-headers.test.js
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Determining test suites to run...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;testing on https://https://your.dom.ain
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; PASS  webserver/security-headers.test.js
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Check security headers
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ✓ 200 /robots.txt (55ms)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ✓ 200 /feed/endpoint/of/interest (408ms)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ✓ 403 /api/other/auth/endpoint (18ms)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ✓ 404 /never/gonna/give/you/up! (6ms)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ✓ 500 /api/dies/for/testing (12ms)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Test Suites: 1 passed, 1 total
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Tests:       5 passed, 5 total
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Snapshots:   0 total
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Time:        2.721s, estimated 3s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Ran all test suites matching /.\/__tests__\/webserver\/security-headers.test.js/i.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This can also be extended to ensure that certain headers do not exist, or do not contain details that you do not want exposed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;Server&lt;/code&gt; header should not reveal the nginx (see &lt;a href=&#34;https://nginx.org/en/docs/http/ngx_http_core_module.html#server_tokens&#34;&gt;server_tokens&lt;/a&gt;) or Apache (see &lt;a href=&#34;https://httpd.apache.org/docs/trunk/mod/core.html#servertokens&#34;&gt;ServerTokens&lt;/a&gt;) version numbers&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;X-Powered-By&lt;/code&gt; header should be absent, not exposing the fact that you are using PHP, and the version number — see the &lt;a href=&#34;https://www.php.net/manual/en/ini.core.php#ini.expose-php&#34;&gt;expose_php&lt;/a&gt; directive for &lt;code&gt;php.ini&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;or with the Java Wildfly server, both of those headers are sent by default! — see instructions on how to omit them &lt;a href=&#34;https://zenidas.wordpress.com/recipes/hideexpose-http-headers-in-wildfly-10-1/&#34;&gt;by editing XML&lt;/a&gt; or &lt;a href=&#34;https://mariusz.wyszomierski.pl/en/turn-off-x-powered-by-i-server-headers-in-wildfly-10/&#34;&gt;using jboss-cli&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Add to the &lt;code&gt;verifs&lt;/code&gt; array in the code above:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { header: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;server&amp;#39;&lt;/span&gt;,                    expect: (x) =&amp;gt; x.not.toMatch(&lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/\d/&lt;/span&gt;)         },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { header: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;x-powered-by&amp;#39;&lt;/span&gt;,              expect: (x) =&amp;gt; x.toBeUndefined()           },&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now if (when) I forget about the nginx &lt;code&gt;add_headers&lt;/code&gt; behavior, make changes, and inadvertently break things? Instead of it being unnoticed, my test suite will alert me so I can fix it before it goes into production!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Salesforce Integration with Node.js</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/03/salesforce-integration-with-node/"/>
      <id>https://www.endpointdev.com/blog/2020/03/salesforce-integration-with-node/</id>
      <published>2020-03-27T00:00:00+00:00</published>
      <author>
        <name>Dylan Wooters</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/03/salesforce-integration-with-node/fulton-ceiling.jpg&#34; alt=&#34;Patterned roof&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;small&gt;Photo by Dylan Wooters, 2020&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;Salesforce is huge. It is currently the dominant customer relationship management (CRM) provider, accounting for &lt;a href=&#34;https://www.forbes.com/sites/louiscolumbus/2019/06/22/salesforce-now-has-over-19-of-the-crm-market/#75a6cdf9333a&#34;&gt;around 20%&lt;/a&gt; of market share. Businesses are using Salesforce not only as a traditional CRM solution, but also for novel purposes. Salesforce can serve as a backend database and admin portal for custom apps, or as a reporting tool that pulls data from various systems.&lt;/p&gt;
&lt;p&gt;This growth leads to increasing demand for Salesforce integrations. The term “Salesforce integration” may conjure up images of expensive enterprise software or dense API documentation, but it doesn’t have to be that way. You can work with Salesforce easily using Node.js and the npm package &lt;a href=&#34;https://jsforce.github.io/&#34;&gt;JSforce&lt;/a&gt;. An example of a project that might benefit from this kind of Node.js integration is an e-commerce website where order data is loaded to and from Salesforce for order fulfillment, tracking, and reporting.&lt;/p&gt;
&lt;p&gt;In this post we’ll cover how to connect to Salesforce using JSforce, the basics of reading and writing data, as well as some advanced topics like working with large amounts of data and streaming data with &lt;a href=&#34;https://socket.io/&#34;&gt;Socket.IO&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;setting-up&#34;&gt;Setting Up&lt;/h3&gt;
&lt;p&gt;You’ll first want to &lt;a href=&#34;https://nodejs.org/en/download/&#34;&gt;install Node.js&lt;/a&gt; on your local machine, if you haven’t done so already.&lt;/p&gt;
&lt;p&gt;Next, create your Node app. This will vary with your requirements. I often use &lt;a href=&#34;https://expressjs.com/&#34;&gt;Express&lt;/a&gt; to build a REST API for integration purposes. Other times, if I am routinely loading data into Salesforce, I will create Node scripts and schedule them using cron. For the purposes of this post, we will create a small Node script that can be run on the command line.&lt;/p&gt;
&lt;p&gt;Create a new directory for your project, and within that directory, run &lt;code&gt;npm init&lt;/code&gt; to generate your package.json file. Then install JSforce with &lt;code&gt;npm install jsforce&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, create a file named script.js, which we will run on the command line for testing. To test the script at any time, simply navigate to your app’s directory and run &lt;code&gt;node script.js&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;At the top of the script, require &lt;code&gt;jsforce&lt;/code&gt;, as well as the Node IO libraries &lt;code&gt;fs&lt;/code&gt; and &lt;code&gt;path&lt;/code&gt;. Then define an asynchronous function that will serve as our script body. This is where all of your Salesforce code will go.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; jsforce = require(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;jsforce&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; fs = require(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;fs&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; path = require(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;path&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;run();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; run() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// salesforce code goes here...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;connecting-to-salesforce&#34;&gt;Connecting to Salesforce&lt;/h3&gt;
&lt;p&gt;I usually store my Salesforce credentials and instance URL as a JSON object in a separate file, which I gitignore. This ensures that sensitive data does not appear in Git. Below is the content of my salesforce-creds.json file. You’ll want to add your Salesforce username and password and update the instance URL, if necessary.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;your&lt;/span&gt; &lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;username&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;password&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;your&lt;/span&gt; &lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;password&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://na111.salesforce.com&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To connect to Salesforce simply retrieve the credentials from the file and use them with the JSforce Connection class to login. Be sure to wrap all JSforce code in a try-catch block, to catch any errors coming back from Salesforce.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; creds = JSON.parse(fs.readFileSync(path.resolve(__dirname, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;./salesforce-creds.json&amp;#39;&lt;/span&gt;)).toString());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; conn = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; jsforce.Connection({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    loginUrl: creds.url
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; conn.login(creds.username, creds.password);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    console.log(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Connected to Salesforce!&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// now you can use conn to read/write data...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; conn.logout();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;catch&lt;/span&gt; (err) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    console.error(err);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;reading-writing-and-deleting-data&#34;&gt;Reading, Writing, and Deleting Data&lt;/h3&gt;
&lt;p&gt;Once connected, the easiest way to query data from Salesforce is to use the JSforce query function, and pass in an &lt;a href=&#34;https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql.htm&#34;&gt;SOQL&lt;/a&gt; statement. This offers the most flexibility, as you can run queries for child and parent objects. Using SOQL, we can query all accounts and their contacts (children) in a single statement. Note, however, that there are &lt;a href=&#34;https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_relationships_query_limits.htm&#34;&gt;limitations on relationship queries&lt;/a&gt;. You can only go down one level, from parent to child, but you can go up multiple levels from child to parent.&lt;/p&gt;
&lt;p&gt;Writing and deleting data is simple with JSforce using the &lt;code&gt;sobject&lt;/code&gt; class and the corresponding create/update/delete function. In the example below, we will query for accounts and contacts using SOQL, and then isolate and update a specific contact using &lt;code&gt;sobject().update&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; soql = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`select id, name,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    (SELECT Id, FirstName, LastName, Email_Verified__c, Enrollment_Status__c from Contacts)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    FROM Account`&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; accounts = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; conn.query(soql);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; cooper = accounts.records
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .filter(x =&amp;gt; x.Name === &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Twin Peaks Sheriff Dept.&amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;].Contacts.records
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .filter(y =&amp;gt; y.FirstName === &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Dale&amp;#39;&lt;/span&gt; &amp;amp;&amp;amp; y.LastName === &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Cooper&amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;console.log(cooper);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Console output:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;//     attributes: {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;//         type: &amp;#39;Contact&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;//         url: &amp;#39;/services/data/v42.0/sobjects/Contact/0033h000001sDzDAAU&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;//     },
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;//     Id: &amp;#39;0033h000001sDzDAAU&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;//     FirstName: &amp;#39;Dale&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;//     LastName: &amp;#39;Cooper&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;//     Email_Verified__c: true,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;//     Enrollment_Status__c: &amp;#39;Pending&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cooper.Enrollment_Status__c = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Accepted&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; ret = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; conn.sobject(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Contact&amp;#39;&lt;/span&gt;).update(cooper);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (ret.success) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    console.log(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Contact updated in Salesforce.&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;working-with-large-amounts-of-data&#34;&gt;Working with Large Amounts of Data&lt;/h3&gt;
&lt;p&gt;You may need to read and write large amounts of data, for example if you are using Salesforce for reporting and loading data to and from other systems.&lt;/p&gt;
&lt;h4 id=&#34;event-driven-querying&#34;&gt;Event-driven Querying&lt;/h4&gt;
&lt;p&gt;The record limit for standard promise-style SOQL querying, as in our example above, is 2000 records. To query more than that, it is best to shift to the event-driven style of querying. This will ensure that all records are successfully retrieved from Salesforce. You can use the &lt;code&gt;maxFetch&lt;/code&gt; property to set the upper limit of records returned. By default, &lt;code&gt;maxFetch&lt;/code&gt; is set to 10,000.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; contacts = [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; soql = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;SELECT Id, FirstName, LastName, Email_Verified__c, Enrollment_Status__c from Contact&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; query = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; conn.query(soql)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .on(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;record&amp;#34;&lt;/span&gt;, (record) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        contacts.push(record);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .on(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;end&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;async&lt;/span&gt; () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        console.log(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`Fetched Contacts. Total records fetched: &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${&lt;/span&gt;contacts.length&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .on(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;error&amp;#34;&lt;/span&gt;, (err) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        console.error(err);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .run({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        autoFetch: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        maxFetch: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;5000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;loading-data-with-the-bulk-api&#34;&gt;Loading Data with the Bulk API&lt;/h4&gt;
&lt;p&gt;Loading a large amount of data into Salesforce is best accomplished through the &lt;a href=&#34;https://jsforce.github.io/document/#bulk-api&#34;&gt;Bulk API&lt;/a&gt; via JSforce. There are a couple good reasons for this approach.&lt;/p&gt;
&lt;p&gt;The Bulk API has better performance over other methods when working with large collections of objects.&lt;/p&gt;
&lt;p&gt;The standard JSforce &lt;code&gt;sobject&lt;/code&gt; create/update/delete functions have a 200 object limit. For operations on large collections, you must divide the total by 200, resulting in many separate API calls. By contrast, the Bulk API only uses a single API call. Since Salesforce imposes &lt;a href=&#34;https://developer.salesforce.com/docs/atlas.en-us.salesforce_app_limits_cheatsheet.meta/salesforce_app_limits_cheatsheet/salesforce_app_limits_platform_api.htm&#34;&gt;API limits&lt;/a&gt;, this makes the Bulk API a better choice.&lt;/p&gt;
&lt;p&gt;Running a bulk operation is simple using the &lt;code&gt;bulk.load&lt;/code&gt; method, which takes three parameters: the Salesforce object type, the operation type, and an array of objects. The method returns an array of objects with success/errors fields, as well as the id of the newly created object, if successful.&lt;/p&gt;
&lt;p&gt;If you’re working with thousands of objects, it’s good to set the pollTimeout property manually to one minute or more, to avoid Salesforce connection timeouts. Also note that the possible values for operation type are: ‘insert’, ‘update’, ‘upsert’, ‘delete’, or ‘hardDelete’.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// set poll timeout to one minute for larger datasets
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;sfConnection.bulk.pollTimeout = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;240000&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// normally you will have thousands of Accounts, this is just an example
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; accounts = [{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Name: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Saul Goodman, LLC&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Name: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Los Pollos Hermanos Inc&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Name: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Hamlin, Hamlin &amp;amp; McGill&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; results = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; conn.bulk.load(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Account&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;insert&amp;#39;&lt;/span&gt;, accounts);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;console.log(results);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Console output:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// [
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;//     { id: &amp;#39;0013h000006bdd2AAA&amp;#39;, success: true, errors: [] },
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;//     { id: &amp;#39;0013h000006bdd3AAA&amp;#39;, success: true, errors: [] },
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;//     { id: &amp;#39;0013h000006bdd4AAA&amp;#39;, success: true, errors: [] }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// ]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (accounts.length === results.filter(x =&amp;gt; x.success).length) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    console.log(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;All account successfully loaded.&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;websocket-streaming-with-socketio&#34;&gt;WebSocket Streaming with Socket.io&lt;/h3&gt;
&lt;p&gt;Say you are building a web application for reporting, and the app contains a dashboard with data on all of your contacts in Salesforce. You want the dashboard to be updated whenever the data in Salesforce changes, and you also want this to happen without refreshing the web page.&lt;/p&gt;
&lt;p&gt;To accomplish this, you can stream real-time data from Salesforce using JSforce and the &lt;a href=&#34;https://socket.io/&#34;&gt;Socket.IO library&lt;/a&gt;, which makes working with WebSockets quite simple.&lt;/p&gt;
&lt;p&gt;The first step in this process is creating a &lt;a href=&#34;https://developer.salesforce.com/docs/atlas.en-us.api_streaming.meta/api_streaming/code_sample_java_create_pushtopic.htm&#34;&gt;PushTopic&lt;/a&gt; in Salesforce. This is basically a trigger that emits a notification anytime an object is created, updated, etc. in Salesforce. I created a PushTopic for Contacts by running the following Apex code in the Salesforce developer console.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PushTopic pushTopic = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; PushTopic();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pushTopic.Name = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;UserChange&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pushTopic.Query = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;SELECT Id, FirstName, LastName, Email_Verified__c, Enrollment_Status__c FROM Contact&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pushTopic.ApiVersion = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;48.0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pushTopic.NotifyForOperationCreate = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pushTopic.NotifyForOperationUpdate = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pushTopic.NotifyForOperationUndelete = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pushTopic.NotifyForOperationDelete = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pushTopic.NotifyForFields = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Referenced&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;insert pushTopic;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, back in your Node app, install Express and Socket.IO.&lt;/p&gt;
&lt;p&gt;Next, you’ll want to create a very basic Express server that will listen for updates from the Salesforce PushTopic, and emit them to your reporting site. Start by installing Express and Socket.IO.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm install express
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm install socket.io&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then delete the &lt;code&gt;run&lt;/code&gt; function in your script.js file, which contained the code from the samples above, and replace it with the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; run() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// listen with express
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    server.listen(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;3000&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        console.log(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;listening on *:3000&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// connect to Salesforce
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; creds = JSON.parse(fs.readFileSync(path.resolve(__dirname, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;./salesforce-creds.json&amp;#39;&lt;/span&gt;)).toString());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; conn = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; jsforce.Connection({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        loginUrl: creds.url
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; conn.login(creds.username, creds.password);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;catch&lt;/span&gt; (err) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        console.error(err);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// when the client connects, emit streaming updates from salesforce to client
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    io.on(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;connection&amp;#34;&lt;/span&gt;, (socket) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        console.log(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;A socket connection was made!&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; eventHandler = (message) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            console.log(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;New streaming event received from Salesforce:&amp;#39;&lt;/span&gt;, message);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            socket.emit(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;UserChange&amp;#39;&lt;/span&gt;, message);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        conn.streaming.topic(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;UserChange&amp;#39;&lt;/span&gt;).subscribe(eventHandler);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here is a step-by-step description of what is occurring in the code sample above:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Express server is set to listen for connections on port 3000.&lt;/li&gt;
&lt;li&gt;We connect to Salesforce and login.&lt;/li&gt;
&lt;li&gt;Socket.IO is set to listen for incoming connections from clients.&lt;/li&gt;
&lt;li&gt;A function called &lt;code&gt;eventHandler&lt;/code&gt; that emits Salesforce streaming messages to the client is defined.&lt;/li&gt;
&lt;li&gt;When a connection is made, &lt;code&gt;eventHandler&lt;/code&gt; is attached to the Salesforce streaming topic as a callback, using the live Salesforce connection.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you follow the nice little &lt;a href=&#34;https://socket.io/get-started/chat/&#34;&gt;tutorial from Socket.IO&lt;/a&gt; and create the sample chat webpage, you can actually test the Salesforce streaming updates. In the chat page, add this script, which will log messages coming back from Salesforce.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; socket = io();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    socket.on(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;UserChange&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;(msg) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        console.log(msg);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then update a contact in Salesforce, changing the contact’s first name. If everything works correctly, you should see the client connect via Socket.IO in the Node logs, and also see a streaming message from Salesforce logged in the browser’s console window.&lt;/p&gt;
&lt;h3 id=&#34;summary&#34;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Node.js and JSforce provide a straightforward and elegant way to interact with Salesforce. Whether you have an existing Node API that needs to work with Salesforce, or you are building a new application that is powered by Salesforce data, consider the recipes above as stepping stones for completing your project.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>GraphQL Server Libraries</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2019/07/graphql-server-libraries/"/>
      <id>https://www.endpointdev.com/blog/2019/07/graphql-server-libraries/</id>
      <published>2019-07-12T00:00:00+00:00</published>
      <author>
        <name>Zed Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2019/07/graphql-server-libraries/image-0.jpg&#34; alt=&#34;Eroded Icelandic mountain&#34; /&gt;&lt;br&gt;Photo by &lt;a href=&#34;https://unsplash.com/photos/t07FAEn9wAA&#34;&gt;Jon Flobrant&lt;/a&gt; on Unsplash&lt;/p&gt;
&lt;p&gt;This post is a followup to my previous post, &lt;a href=&#34;/blog/2019/05/graphql-an-alternative-to-rest/&#34;&gt;GraphQL — An Alternative to REST&lt;/a&gt;. Please check that out for an introduction to GraphQL and what makes it different from other API solutions. I’ve collected a list of some of the currently-maintained GraphQL libraries for a few different languages, along with some examples (most of which aren’t fully functional on their own, they’d need more configuration) so you can see what it might be like to use GraphQL in your project. I’ll be focusing on the ways each of these libraries implement GraphQL and what you’d need to do to start a project with each of them, so if you have questions about GraphQL itself, please check out my other blog post.&lt;/p&gt;
&lt;h3 id=&#34;apollo-server-javascripttypescript&#34;&gt;Apollo Server (JavaScript/TypeScript)&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://www.apollographql.com/&#34;&gt;Apollo GraphQL&lt;/a&gt; has libraries for both a GraphQL server and client (which I’ll discuss later). &lt;a href=&#34;https://www.apollographql.com/docs/apollo-server/&#34;&gt;Apollo Server&lt;/a&gt; can be used both as a standalone server as well as with libraries like &lt;a href=&#34;https://expressjs.com/&#34;&gt;Express&lt;/a&gt;. Apollo Server is the server library I have the most experience with—I wrote a server last year using Express and Apollo Server, along with a client that used Apollo Client. I’m a fan of the flexibiliy of Apollo, but it takes more work to set up than some of the alternatives.&lt;/p&gt;
&lt;p&gt;Setting up Apollo Server as a standalone can be done fairly simply following the directions on &lt;a href=&#34;https://www.apollographql.com/docs/apollo-server/getting-started/&#34;&gt;their website&lt;/a&gt;. However, I’m going to go over the basics of integrating with Express. There are two main parts to writing a server with Apollo: your GraphQL schema and your resolvers. These stay more or less the same whether you’re using Apollo as a standalone server or combining it with Express. You do have to have a database set up separately; I’ll show examples with MongoDB, but you could easily swap it out with PostgreSQL or another database. I’ll show an example resolver along with its GraphQL schema for a blog post. The schema follows the GraphQL schema rules and might look like the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; typeDefs = [gql&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  type Post {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    id: String!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    body: String!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  query {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    post(id: String!): Post
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`&lt;/span&gt;];&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now for the resolver. Resolvers are functions that take information from the query (like arguments) and return the relevant data, usually from a database. For our blog post, a resolver might look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; resolvers = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  post: (root, args, context, info) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; Post.findById(args.id);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Simple! We just get the data from the database and return it—as long as the property names match those of our schema, Apollo will automatically format it according to the frontend’s request and return it to them.&lt;/p&gt;
&lt;p&gt;OK, next we create the server:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; express from &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;express&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; { ApolloServer } from &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;apollo-server-express&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; PORT = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;3000&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; app = express();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; server = ApolloServer({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  typeDefs,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  resolvers
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;server.applyMiddleware(app);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app.listen(PORT, () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  console.log(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`Server running at http://localhost:&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${&lt;/span&gt;PORT&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/graphql`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And that’s it! Note that these examples are missing a few things like imports, and we didn’t add authentication of any kind, but this is the general format for creating a server with Apollo.&lt;/p&gt;
&lt;h3 id=&#34;prisma&#34;&gt;Prisma&lt;/h3&gt;
&lt;p&gt;Prisma is a cool library developed by the same people as Graph.cool that does much of the work for you in enabling GraphQL access to your database.&lt;/p&gt;
&lt;p&gt;Prisma offers configuration for existing databases, but unfortunately I had trouble getting it to work on my Ubuntu system—I ran into issues getting the Docker container to connect to my local Postgres and MongoDB databases. However, following the quick guide found &lt;a href=&#34;https://www.prisma.io/docs/get-started/01-setting-up-prisma-new-database-JAVASCRIPT-a002/&#34;&gt;here&lt;/a&gt; on the Prisma website, I was able to get a GraphQL server up and running inside a Docker container with a new database. The process was simple:&lt;/p&gt;
&lt;p&gt;First, you have to install the Prisma command line utility:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm install -g prisma&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You also need to have Docker installed. Documentation for Docker can be found &lt;a href=&#34;https://docs.docker.com/get-started/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Next, you need to configure Prisma. Create a directory for your Prisma server, and create a new file named &lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir hello-world
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd hello-world
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;touch docker-compose.yml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, paste the following into it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;version: &amp;#39;3&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;services:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  prisma:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    image: prismagraphql/prisma:1.34
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    restart: always
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ports:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &amp;#39;4466:4466&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    environment:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      PRISMA_CONFIG: |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        port: 4466
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        databases:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          default:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            connector: mongo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            uri: mongodb://prisma:prisma@mongo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  mongo:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    image: mongo:3.6
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    restart: always
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    environment:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      MONGO_INITDB_ROOT_USERNAME: prisma
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      MONGO_INITDB_ROOT_PASSWORD: prisma
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ports:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &amp;#39;27017:27017&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    volumes:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - mongo:/var/lib/mongo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;volumes:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  mongo: ~&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I used Mongo here, but Prisma’s site has guides for PostgreSQL and MySQL as well. It’s important to make sure now that you don’t have any conflicts with currently running databases—on my machine I had already had a MongoDB server running on port 27017. I fixed this by just stopping my local MongoDB server, but I’m sure you could configure the Docker containers to work with different ports as well. Running Ubuntu, I just ran &lt;code&gt;sudo service mongodb stop&lt;/code&gt; and then the Prisma Docker containers worked just fine. When I was done, I ran &lt;code&gt;sudo service mongodb start&lt;/code&gt; to start it up again.&lt;/p&gt;
&lt;p&gt;Next, you’ll start the Prisma containers and initialize the Prisma server configuration:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose up -d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;prisma init --endpoint http://localhost:4466&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The final step is to deploy the service:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;prisma deploy&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If all goes well, you’ll see a message that includes a URL to the Prisma Admin, which is a browser tool to interact with your GraphQL endpoints. I used it for a little while when I was testing Prisma out, and it seems to work well and is easy to use.&lt;/p&gt;
&lt;p&gt;All in all, Prisma seems like a great way to start if you don’t want to handle the messy details of setup. However, I did have issues getting it to play nice with my already-existing databases (including both PostgreSQL and MongoDB). However, it is still relatively new, so I would expect support to get better over time.&lt;/p&gt;
&lt;h3 id=&#34;graphene-python&#34;&gt;Graphene (Python)&lt;/h3&gt;
&lt;p&gt;Graphene is a GraphQL framework for Python. It has integrations for a few different server frameworks (a list can be found &lt;a href=&#34;https://github.com/graphql-python/graphene&#34;&gt;here&lt;/a&gt;), but I’ll show examples from &lt;code&gt;graphene-django&lt;/code&gt;, since Django is fairly common and something that we use fairly often at End Point.&lt;/p&gt;
&lt;p&gt;Because you’re also setting up a Django project, the tutorial for graphene-django is a little more involved, so I’ll just share the relevant GraphQL sections so you can compare to the other libraries in this post. The most important part, the schema, is defined in Python with a similar format to Django models:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import graphene
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;from graphene_django.types import DjangoObjectType
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;from app.models import Category, Ingredient
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;class CategoryType(DjangoObjectType):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    class Meta:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        model = Category
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;class IngredientType(DjangoObjectType):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    class Meta:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        model = Ingredient
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;class Query(object):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    all_categories = graphene.List(CategoryType)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    all_ingredients = graphene.List(IngredientType)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    def resolve_all_categories(self, info, **kwargs):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        return Category.objects.all()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    def resolve_all_ingredients(self, info, **kwargs):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        # We can easily optimize query count in the resolve method
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        return Ingredient.objects.select_related(&amp;#39;category&amp;#39;).all()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, the format for defining your GraphQL schema is quite different from some other libraries, but you have the advantage of it looking similar to Django’s model definitions. You’ll also need a higher-level Query definition:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import graphene
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import cookbook.ingredients.schema
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;class Query(cookbook.ingredients.schema.Query, graphene.ObjectType):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    # This class will inherit from multiple Queries
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    # as we begin to add more apps to our project
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pass
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;schema = graphene.Schema(query=Query)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now that we have a schema defined, we need to add a few things to &lt;code&gt;settings.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INSTALLED_APPS = [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    # This will also make the `graphql_schema` management command available
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#39;graphene_django&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GRAPHENE = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#39;SCHEMA&amp;#39;: &amp;#39;cookbook.schema.schema&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The last piece needed to use your GraphQL schema is in &lt;code&gt;urls.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;from graphene_django.views import GraphQLView
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;urlpatterns = [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url(r&amp;#39;^admin/&amp;#39;, admin.site.urls),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url(r&amp;#39;^graphql$&amp;#39;, GraphQLView.as_view(graphiql=True)),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And finally we run the server:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ python manage.py runserver&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now you should be able to use your GraphQL schema at http://localhost:8000/graphql just like with any other GraphQL server.&lt;/p&gt;
&lt;p&gt;Graphene for Django seems like a good solution in that it uses a similar format to other aspects of Django, like the model definitions. However, its format (especially for schema definition) is rather different-looking from the GraphQL standard used by most other libraries, and it seems like it might make it more work to keep your frontend and backend in sync.&lt;/p&gt;
&lt;h3 id=&#34;graphcool&#34;&gt;Graph.cool&lt;/h3&gt;
&lt;p&gt;I won’t discuss Graph.cool in detail here, because I went over it in my previous blog post. However, it still merits mention here as an option for your GraphQL server. Essentially, Graph.cool lets you define a GraphQL schema and then handles the work of setting up a database and even hosting for you. If you just want to get a basic GraphQL server set up for testing, or if you don’t need too many features beyond data storage and retrieval, Graph.cool is a great choice.&lt;/p&gt;
&lt;h3 id=&#34;additional-links&#34;&gt;Additional links&lt;/h3&gt;
&lt;p&gt;For server libraries in other languages, these seem like good options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://graphql-ruby.org/&#34;&gt;GraphQL Ruby&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.graphql-java.com/&#34;&gt;GraphQL Java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/graph-gophers/graphql-go&#34;&gt;graphql-go&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thanks for reading! Keep an eye out next week for a second post which will cover GraphQL client libraries.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>GraphQL — An Alternative to REST</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2019/05/graphql-an-alternative-to-rest/"/>
      <id>https://www.endpointdev.com/blog/2019/05/graphql-an-alternative-to-rest/</id>
      <published>2019-05-11T00:00:00+00:00</published>
      <author>
        <name>Zed Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2019/05/graphql-an-alternative-to-rest/banner.jpg&#34; alt=&#34;Banner&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://graphql.org/&#34;&gt;GraphQL&lt;/a&gt; has become more and more popular recently as an alternative to traditional RESTful APIs since it was released as open source by Facebook in 2015. According to the GraphQL website, it is “a query language for APIs and a runtime for fulfilling those queries with your existing data”. In this blog post, I’ll go over some of what makes GraphQL different from other API solutions, and then show how to get a GraphQL API up and running so you can try it out yourself!&lt;/p&gt;
&lt;p&gt;GraphQL is designed to fit on top of your database layer. With the help of libraries like &lt;a href=&#34;https://www.apollographql.com/&#34;&gt;Apollo GraphQL&lt;/a&gt;, it can be used with many different databases. Some of the main differences between GraphQL and more traditional RESTful APIs include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;GraphQL uses one endpoint. Most traditional APIs use an endpoint for each type of data; in my example, you’d probably have one each for users (&lt;code&gt;/user&lt;/code&gt;), posts (&lt;code&gt;/post&lt;/code&gt;) and comments (&lt;code&gt;/comment&lt;/code&gt;). Each of these would return some JSON with the data you want. GraphQL, on the other hand, lives at one endpoint (usually &lt;code&gt;/graphql&lt;/code&gt;) and changes what it returns based on what you ask for, as detailed in the next point.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can get multiple types in one request. For instance, if you want to get information about an author plus all of their posts, instead of making a request for the author and a request for posts, you do just one request for the author and specify that you’d like their posts as well:&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;query {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  user(id: &amp;#34;12345&amp;#34;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    posts {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      title
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      body
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;You decide which parts of the data you want. Traditional REST APIs give you data based on which endpoint you’re querying (&lt;code&gt;/post/:id&lt;/code&gt;, &lt;code&gt;/user/:id&lt;/code&gt;, etc.), and the format of the data is generally the same. For instance, no matter which &lt;code&gt;id&lt;/code&gt; you ask for at &lt;code&gt;/posts/:id&lt;/code&gt;, you’ll always get something that looks like this back:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;123&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Smash Mouth&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;joined&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;1994&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But what if we don’t need to know when they joined right now? Another example that better illustrates this problem (and GraphQL’s solution) is getting a list of blog posts. The home page of a blog usually shows several of the most recent posts, but you can also view a list of just post names that will go farther back. With a traditional REST API, you would have to create separate endpoints for each of these scenarios, for example &lt;code&gt;/first_posts&lt;/code&gt; and &lt;code&gt;/posts&lt;/code&gt;, or add GET parameters or similar. With GraphQL, you can just specify exactly what you want. The query to replace &lt;code&gt;/first_posts&lt;/code&gt; might look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;query {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  posts {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    title
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    body
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    author {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      name
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The data returned might look like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;posts&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;All Star&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;body&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Somebody once told me the world is gonna roll me\n I ain&amp;#39;t the sharpest tool in the shed\n She was looking kind of dumb with her finger and her thumb\n In the shape of an \&amp;#34;L\&amp;#34; on her forehead\n Well the years start coming and they don&amp;#39;t stop coming\n Fed to the rules and I hit the ground running\n Didn&amp;#39;t make sense not to live for fun\n Your brain gets smart but your head gets dumb\n So much to do, so much to see\n So what&amp;#39;s wrong with taking the back streets?\n You&amp;#39;ll never know if you don&amp;#39;t go\n You&amp;#39;ll never shine if you don&amp;#39;t glow\n Hey now, you&amp;#39;re an all-star, get your game on, go play\n Hey now, you&amp;#39;re a rock star, get the show on, get paid\n And all that glitters is gold\n Only shooting stars break the mold\n Only shooting stars break the mold&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;author&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Smash Mouth&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Less-interesting Stuff&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;body&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Sed ut perspiciatis, unde omnis iste natus error sit voluptatem\n accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore\n veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam\n voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur\n magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui\n dolorem ipsum, quia dolor sit amet consectetur adipisci[ng] velit, sed quia non-numquam\n [do] eius modi tempora inci[di]dunt, ut labore et dolore magnam aliquam quaerat\n voluptatem. Ut enim ad minima veniam, quis nostrum[d] exercitationem ullam corporis\n suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure\n reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel\n illum, qui dolorem eum fugiat, quo voluptas nulla pariatur?&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;author&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Some Latin-speaking Guy&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That’s great for &lt;code&gt;/first_posts&lt;/code&gt;, but we probably don’t need to bloat the response with the entire post body if we’re just using the names for a list, right? Let’s try again, but this time we’ll remove &lt;code&gt;body&lt;/code&gt; from the request:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;query {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  posts {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    title
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    author {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      name
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now we just get the title and author, exactly like we want. Notice also that these queries are pulling in data from a related model, the User model, via the &lt;code&gt;author&lt;/code&gt; property.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;posts&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;All Star&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;author&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Smash Mouth&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Less-interesting Stuff&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;author&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Some Latin-speaking Guy&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Although you can get similar functionality by using GET parameters or creating extra endpoints, with GraphQL, you can just make that one simple change without needing to write extra code.&lt;/p&gt;
&lt;h3 id=&#34;try-it&#34;&gt;Try it!&lt;/h3&gt;
&lt;p&gt;OK, so there are some of the things that sets GraphQL apart. However, reading about it is only so helpful—now I’ll show how to get a GraphQL backend up and running quickly using &lt;a href=&#34;https://www.graph.cool/&#34;&gt;Graph.cool&lt;/a&gt;, a Node.js backend service that does most of the work of setting up a server, including setting up a database based on your schema. (Graphcool was retired in mid-2020, so you should instead check out their more flexible server option called &lt;a href=&#34;https://www.prisma.io/&#34;&gt;Prisma&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;First, you need to install graphcool by running:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npm install -g graphcool&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;You’ll also need to sign up for a (free) account, but you can do that later. Once graphcool is installed, create a directory to use for your code and initialize it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ mkdir gql-blog
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ &lt;span style=&#34;color:#038&#34;&gt;cd&lt;/span&gt; gql-blog
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ graphcool init&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Open the file &lt;code&gt;types.graphql&lt;/code&gt; and uncomment the lines describing a model called Post, as well as the related line in User; aftewards, it should look something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;type User @model {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  # id is of type ID, must be not null (!), and must be unique
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  id: ID! @isUnique
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  name: String
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  dateOfBirth: DateTime
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  # posts contains an array of Post objects
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  posts: [Post!]! @relation(name: &amp;#34;UserPosts&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;type Post @model {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  id: ID! @isUnique
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  title: String!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  # Graphcool relations have to be defined both ways so the service knows
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  # whether it&amp;#39;s a one-to-many, many-to-many, or many-to-one relation
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  author: User! @relation(name: &amp;#34;UserPosts&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that there are a few extra descriptors here that aren’t part of the core GraphQL schema; &lt;code&gt;@isUnique&lt;/code&gt;, &lt;code&gt;@relation&lt;/code&gt;, &lt;code&gt;@model&lt;/code&gt;, and &lt;code&gt;ID&lt;/code&gt; are all part of the Graphcool service’s additions. However, they are allowed within GraphQL’s spec and provide some extra functionality to the API. &lt;a href=&#34;https://graphql.org/learn/schema/#type-system&#34;&gt;GraphQL’s website&lt;/a&gt; has more information about the type system.&lt;/p&gt;
&lt;p&gt;Now that you have a basic schema, it’s time to run your server. In the directory where you ran &lt;code&gt;graphcool init&lt;/code&gt;, run &lt;code&gt;graphcool deploy&lt;/code&gt;. First, it’ll open a browser window and ask you to log in or create an account. Once you do that, you should see a prompt asking you which server cluster you want to deploy to. Pick any of them (but note that &lt;code&gt;local&lt;/code&gt; requires a bit more setup before working). Push enter on the next few prompts to use the default options and you’ll get a big wall of text telling you that your server is up and running! To try it out, find the line near the end that looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Here are your GraphQL Endpoints:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Simple API:        https://api.graph.cool/simple/v1/bbd987478jjhhds902k2l&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Copy and paste that URL into a browser and you’ll have access to the GraphQL Playground, where you can run queries and mutations (like queries, but for creating/updating data). You need some data to play with, so copy the following into the Playground one at a time and run them to create a User and a Post. Note that you’ll need to look at the ID returned by the first mutation and paste it into the second mutation so that the two are linked.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mutation {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  createUser(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name: &amp;#34;SmashMouth&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    id
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mutation {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  createPost(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    title: &amp;#34;All Star&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    authorId: &amp;#34;USER_ID_HERE&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    id
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    title
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once those are finished, you can run queries to get the data you want. Here’s an example of a query to return the author you created earlier plus any posts associated with it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;query {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  User(id: &amp;#34;USER_ID_HERE&amp;#34;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    id
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    posts {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      id
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You’ll notice that this query only returns the ID of the user and their posts. That’s because we only asked for the ID—if you want to get the name of the user and the title of the post, you’ll have to add that to the query:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;query {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  User(id: &amp;#34;USER_ID_HERE&amp;#34;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    id
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    posts {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      id
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      title
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Pretty cool, huh? You get exactly what you ask for. Next, try getting your post (you’ll need its ID) and asking for its author along with it..&lt;/p&gt;
&lt;p&gt;On the far right of the GraphQL Playground, there’s a tab labeled Schema that shows the entire schema of your app. If you look in there, you’ll see that there are far more types defined than were in the &lt;code&gt;types.graphql&lt;/code&gt; file we edited earlier—this is because graphcool automatically generates them for you. That’s the main reason why it’s easier to use than other GraphQL libraries. In particular, you might try getting all posts or all users.&lt;/p&gt;
&lt;p&gt;If you want to keep messing around with GraphQL, I’d suggest trying to add your own new &lt;code&gt;Comment&lt;/code&gt; type to types.graphql, emulating the way User and Post are related to each other. Keep in mind that you’ll have to run &lt;code&gt;graphcool deploy&lt;/code&gt; and reload your browser to see your changes show up in the Playground.&lt;/p&gt;
&lt;p&gt;For more information and thorough documentation, see the &lt;a href=&#34;https://graphql.org/learn/&#34;&gt;GraphQL website&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you want to try GraphQL without installing anything, visit &lt;a href=&#34;http://graphql.nodaljs.com/&#34;&gt;graphql.nodaljs.com&lt;/a&gt; which lets you try it on their own server.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Extensible Binary Encoding with CBOR</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2019/03/extensible-binary-encoding-with-cbor/"/>
      <id>https://www.endpointdev.com/blog/2019/03/extensible-binary-encoding-with-cbor/</id>
      <published>2019-03-18T00:00:00+00:00</published>
      <author>
        <name>Matt Vollrath</name>
      </author>
      <content type="html">
        &lt;img src=&#34;/blog/2019/03/extensible-binary-encoding-with-cbor/convert-crop.png&#34; alt=&#34;illustration of man converting something in a machine&#34; /&gt;
&lt;p&gt;CBOR is a relatively new IETF draft standard extensible binary data format. Compared to similar formats like MessagePack and BSON, CBOR was developed from the ground up with clear goals:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;unambiguous encoding of most common data formats from Internet standards&lt;/li&gt;
&lt;li&gt;code compactness for encoder and decoder&lt;/li&gt;
&lt;li&gt;no schema description needed&lt;/li&gt;
&lt;li&gt;reasonably compact serialization&lt;/li&gt;
&lt;li&gt;applicability to constrained and unconstrained applications&lt;/li&gt;
&lt;li&gt;good JSON conversion&lt;/li&gt;
&lt;li&gt;extensibility&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;—&lt;a href=&#34;https://tools.ietf.org/html/rfc7049&#34;&gt;RFC 7049&lt;/a&gt; Appendix E, Copyright © 2013 IETF Trust, Bormann &amp;amp; Hoffman&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;In the context of data storage and messaging, most developers can relate to CBOR as a binary drop-in replacement for JSON. While CBOR doesn’t share the human readability of JSON, it can efficiently and unambiguously encode types of data that JSON struggles with. CBOR can also be extended with tags to optimize serialization beyond its standard primitives.&lt;/p&gt;
&lt;h3 id=&#34;encoding-binary-data&#34;&gt;Encoding Binary Data&lt;/h3&gt;
&lt;p&gt;JSON is a ubiquitous data format for web and beyond, for many good reasons, but encoding blobs of binary data is an area where JSON falters. For example, if you are designing a JSON protocol to wrap the storage or transfer of arbitrary objects, your options are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Require that all input data can be represented as JSON. When possible this is potentially a reasonable solution, but limits the types of data that can be encoded. Notable exceptions include most popular image encodings, excluding SVG.&lt;/li&gt;
&lt;li&gt;Base64 encode any binary data values to a string. This can encode any binary data, but increases the size of the data by a minimum of 1/3, incurs encoding and decoding cost, and requires magic to indicate that the string is Base64 encoded.&lt;/li&gt;
&lt;li&gt;Encode the bytes as an array of numbers or a hex string. These are probably not things you should do, but it seemed worth mentioning that these techniques increase the size of the data by anywhere from 2x to 5x and also require magic to indicate that the data is really binary.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With CBOR, binary blobs of any length are supported out of the box and are encoded 1:1. Encoding and decoding CBOR byte strings is extremely fast, even in higher-level languages. In the case of JavaScript, CBOR byte strings can be transcoded to and from the very fast and efficient &lt;code&gt;Uint8Array&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For example, let’s try encoding a simple object with two fields: “name” and “data”. An abstract view of this object:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;name: &amp;#34;Strawberry Pie&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;data: &amp;lt;00 01 02 03 04 05 06 07 08 09&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A C struct of this raw data would be absolute minimum of 24 bytes.&lt;/p&gt;
&lt;p&gt;If you Base64 the data into JSON, your output might look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Strawberry Pie&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;AAECAwQFBgcICQ==&amp;#34;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Length: 51 bytes.&lt;/p&gt;
&lt;p&gt;This illustrates the “magic” problem with Base64 encoding binary data in JSON. Unless you have a JSON schema or special protocol fields, the decoder has no indication that the data in this object needs to be Base64 decoded, because it looks like a string. It is not self-describing.&lt;/p&gt;
&lt;p&gt;The CBOR representation of the same input data, as a hex string:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;a2646e616d656e5374726177626572727920506965696a7065675f646174614a00010203040506070809&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Whoa, there. What do all these numbers and letters mean? I miss my JSON! The Node &lt;a href=&#34;https://www.npmjs.com/package/cbor&#34;&gt;cbor&lt;/a&gt; package has a handy &lt;a href=&#34;https://github.com/hildjj/node-cbor/blob/master/bin/cbor2comment&#34;&gt;&lt;code&gt;cbor2comment&lt;/code&gt; tool&lt;/a&gt; to annotate this hex string for us.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  a2                -- Map, 2 pairs
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    64              -- String, length: 4
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      6e616d65      -- {Key:0}, &amp;#34;name&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    6e              -- String, length: 14
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      5374726177626572727920506965 -- {Val:0}, &amp;#34;Strawberry Pie&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    64              -- String, length: 4
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      64617461 -- {Key:1}, &amp;#34;data&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    4a              -- Bytes, length: 10
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      00010203040506070809 -- {Val:1}, 00010203040506070809&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Length: 35 bytes.&lt;/p&gt;
&lt;p&gt;Now let’s &lt;a href=&#34;https://github.com/mvollrath/cbor-bench&#34;&gt;benchmark JSON against CBOR using real JPEG data&lt;/a&gt;. For this test I used a &lt;a href=&#34;https://github.com/mvollrath/cbor-js/tree/fast_byte_array_encoding&#34;&gt;modified version of cbor-js&lt;/a&gt;, a library compatible with both Node and browsers, and encoded a 910,226 byte JPEG of strawberry rhubarb pie.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                           | JSON      | CBOR    |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| :------------------------ | --------: | ------: |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| Median encoding time (ms) | 3.983     | 0.538   |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| Median decoding time (ms) | 3.151     | 0.006   |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| Encoded size (bytes)      | 1,213,676 | 910,262 |&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As the numbers show, CBOR is both faster and more concise for this particular data. Also, CBOR pie tastes better.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/03/extensible-binary-encoding-with-cbor/strawberry_pie.jpg&#34; alt=&#34;strawberry rhubarb pie&#34;&gt;&lt;/p&gt;
&lt;p&gt;Food always looks good in pictures!&lt;/p&gt;
&lt;h3 id=&#34;optimizing-cbor-with-tags&#34;&gt;Optimizing CBOR with Tags&lt;/h3&gt;
&lt;p&gt;In the case of encoding homogeneous numeric arrays, CBOR encoders can struggle with optimizing the packing of the data. For example, if you have an array of floating point numbers in a higher-level language like Python or JavaScript, the CBOR encoder implementation won’t necessarily determine how many bits are required to encode the numbers, defaulting to the largest available. Additionally, each value in the array will be individually described as a floating point number. This increases the cost and size of the data considerably.&lt;/p&gt;
&lt;p&gt;CBOR has an answer to this problem. The &lt;a href=&#34;https://datatracker.ietf.org/doc/draft-ietf-cbor-array-tags/?include_text=1&#34;&gt;Draft Typed Array Tags&lt;/a&gt; spec includes tags specifying typed arrays which happen to match JavaScript &lt;code&gt;TypedArray&lt;/code&gt; flavors.&lt;/p&gt;
&lt;p&gt;For example, let’s say you have a &lt;code&gt;Float32Array&lt;/code&gt; with a few values:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; floats = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Float32Array([&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1.234567&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2.345678&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;3.456789&lt;/span&gt;]);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;JSON has a really funny way of encoding a &lt;code&gt;Float32Array&lt;/code&gt;, lookit all those bytes:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1.2345670461654663&lt;/span&gt;,&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2.3456780910491943&lt;/span&gt;,&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;2&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;3.456789016723633&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Length: 69 bytes.&lt;/p&gt;
&lt;p&gt;If we were to help the encoder by sending it a regular &lt;code&gt;Array&lt;/code&gt; it would still be pretty verbose, but the precision we weren’t using is truncated, so the overall length will vary wildly depending on the values in the &lt;code&gt;Array&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[1.234567,2.345678,3.456789]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Length: 28 bytes.&lt;/p&gt;
&lt;p&gt;The well-meaning Node cbor library can encode the &lt;code&gt;Float32Array&lt;/code&gt; directly, but doesn’t try to optimize for size:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  83                -- Array, 3 items
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fb              -- Float, next 8 bytes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      3ff3c0c960000000 -- [0], 1.2345670461654663
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fb              -- Float, next 8 bytes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      4002c3f2e0000000 -- [1], 2.3456780910491943
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fb              -- Float, next 8 bytes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      400ba78100000000 -- [2], 3.456789016723633&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Length: 28 bytes.&lt;/p&gt;
&lt;p&gt;Look at all that wasted precision, 64 bits for each 32-bit float, this just won’t do! The CBOR spec allows you to “tag” data for special treatment. According to the &lt;a href=&#34;https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml&#34;&gt;list of registered CBOR tags&lt;/a&gt;, “IEEE 754 binary32, little endian, Typed Array” is tag 85. The consecutive bytes of the three numbers follow.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  d8                --  next 1 byte
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    55              -- Tag #85
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      4c            -- Bytes, length: 12
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        4b069e3f971f1640083c5d40 -- 4b069e3f971f1640083c5d40&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Length: 15 bytes.&lt;/p&gt;
&lt;p&gt;The contained 12 byte string is equivalent to the values in the ArrayBuffer underneath our Float32Array.&lt;/p&gt;
&lt;p&gt;The CBOR decoder will spit out the tag alongside its associated blob, so our optimization is self-described. Now we need to ensure that a byte string with this tag is correctly converted to the typed array, minding endianness. In a mature JavaScript implementation, this is both easy and very fast. Because we encoded the values of the &lt;code&gt;ArrayBuffer&lt;/code&gt; underneath a &lt;code&gt;Float32Array&lt;/code&gt;, we can construct a new &lt;code&gt;Float32Array&lt;/code&gt; from the &lt;code&gt;ArrayBuffer&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; floats = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Float32Array(bytes);  &lt;span style=&#34;color:#888&#34;&gt;// assuming platform and bytes are same endianness!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is a very scalable way to read and write arrays of numeric values into JavaScript.&lt;/p&gt;
&lt;h3 id=&#34;when-to-use-cbor&#34;&gt;When to Use CBOR&lt;/h3&gt;
&lt;p&gt;As &lt;a href=&#34;https://youtu.be/9FBmHB-YoQw?t=57&#34;&gt;King Crimson teaches us&lt;/a&gt;, “it doesn’t mean you should / just because you can.”&lt;/p&gt;
&lt;p&gt;I’ve found CBOR useful as an alternative to JSON for large (&amp;gt;2kB) chunks of raw data or numeric arrays. In many other cases JSON is equivalent or superior, because most languages have native JSON encoding and decoding in their standard libraries. CBOR encoders are not always so optimized, but the gap is closing with wider adoption. Run your own tests and benchmarks on real data against real libraries before deciding to use CBOR.&lt;/p&gt;
&lt;p&gt;Many readers will recognize that schemaful formats such as protocol buffers are an endgame for structured data. If the infrastructure demands make sense for your application, this is also a good way to go. If you’re working with an application that already uses JSON, the difference in development and maintenance costs of porting it to CBOR or protobuf-likes should be measured against the size and performance gains of each approach.&lt;/p&gt;
&lt;p&gt;It’s tempting to let numbers alone decide what format to use, but don’t underestimate the value of JSON’s human readability for structured data. The value of human readability diminishes quickly when dealing with large numeric arrays or binary blobs, so once again CBOR is an appealing choice for these data.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>img.bi, a secret encrypted image sharing service tool</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/07/imgbi-secret-encrypted-image-sharing/"/>
      <id>https://www.endpointdev.com/blog/2015/07/imgbi-secret-encrypted-image-sharing/</id>
      <published>2015-07-30T00:00:00+00:00</published>
      <author>
        <name>Emanuele “Lele” Calò</name>
      </author>
      <content type="html">
        &lt;p&gt;After a fairly good experience with &lt;a href=&#34;https://github.com/atoponce/d-note&#34;&gt;dnote&lt;/a&gt; installed on our own servers as an encrypted notes sharing service, my team decided that it would have been nice to have a similar service for images.&lt;/p&gt;
&lt;p&gt;We found a nice project called &lt;a href=&#34;https://github.com/imgbi/img.bi&#34;&gt;img.bi&lt;/a&gt; that is based on &lt;strong&gt;NodeJS&lt;/strong&gt;, &lt;strong&gt;Python&lt;/strong&gt;, &lt;strong&gt;Redis&lt;/strong&gt; and a lot of client-side &lt;strong&gt;JavaScript&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The system is divided into two components: the &lt;strong&gt;HTML/JS&lt;/strong&gt; frontend and a &lt;strong&gt;Python FastCGI API&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Unfortunately the documentation is a still in its very early stage and it’s lacking a meaningful structure and a lot of needed information.&lt;/p&gt;
&lt;p&gt;Here’s an overview of the steps we followed to setup img.bi on our own server behind &lt;strong&gt;nginx&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;First of all we chose that we wanted to have as much as possible running and confined to a regular user, which is always a good idea with such young and potentially vulnerable tools. We chose to use the &lt;em&gt;imgbi&lt;/em&gt; user.&lt;/p&gt;
&lt;p&gt;Then since we wanted to keep as clean as possible the root user environment (and system status), we also decided to use &lt;strong&gt;pyenv&lt;/strong&gt;. To be conservative we chose the latest Python 2.7 stable release, &lt;em&gt;2.7.10&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git clone https://github.com/yyuu/pyenv.git ~/.pyenv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;echo &amp;#39;export PYENV_ROOT=&amp;#34;$HOME/.pyenv&amp;#34;&amp;#39; &amp;gt;&amp;gt; ~/.bash_profile
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;echo &amp;#39;export PATH=&amp;#34;$PYENV_ROOT/bin:$PATH&amp;#34;&amp;#39; &amp;gt;&amp;gt; ~/.bash_profile
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;echo &amp;#39;eval &amp;#34;$(pyenv init -)&amp;#34;&amp;#39; &amp;gt;&amp;gt; ~/.bash_profile
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;echo $SHELL -l
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pyenv install -l  | grep 2\\.7
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pyenv install 2.7.10
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pyenv global 2.7.10
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pyenv version
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;which python
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In order to use img.bi, we also needed NodeJS and following the same approach we chose to use &lt;a href=&#34;https://github.com/creationix/nvm&#34;&gt;nvm&lt;/a&gt; and install the latest NodeJS stable version:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.25.4/install.sh | bash
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;nvm install stable
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;nvm list
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;nvm use stable
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;nvm alias default stable
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;node --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As a short note to the usage of the &lt;strong&gt;bad practice&lt;/strong&gt; of blindly using:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -o- https://some_obscure_link_or_not | bash&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We want to add that we do &lt;strong&gt;not&lt;/strong&gt; endorse this practice as it’s dangerous and exposes your system to many security risks. On the other hand, though, it’s true that cloning the source via Git and compile/installing it blindly is not much safer, so it’s always up to how much you trust the peer review on the project you’re about to use. And at least with an https URL you should be talking to the destination you want, whereas an http URL is much more dangerous.&lt;/p&gt;
&lt;p&gt;Furthermore going through the entire Python and NodeJS installation as a regular user, was far beyond the scope of this post and the steps proposed here &lt;em&gt;assumes&lt;/em&gt; that you’re doing everything as the regular user, except where specifically stated differently.&lt;/p&gt;
&lt;p&gt;Anyway after that we updated &lt;strong&gt;pip&lt;/strong&gt; and then installed all the needed Python modules:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install --upgrade pip
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install redis m2crypto web.py bcrypt pysha3 zbase62 pyutil flup&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then it’s time to clone the actual img.bi code from the &lt;strong&gt;GitHub&lt;/strong&gt; repo, install a few missing dependencies and then use the bower and npm .json files to add the desired packages:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git clone https://github.com/imgbi/img.bi.git
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd img.bi/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm install -g bower grunt grunt-cli grunt-multiresize
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm install -g grunt-webfont --save-dev
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm install
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bower install&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We also faced an issue which made &lt;strong&gt;Grunt&lt;/strong&gt; fail to start correctly. Grunt was complaining about an “undefined property” called “prototype”. If you happen to have the same problem just type&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd node_modules/grunt-connect-proxy/node_modules/http-proxy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm install eventemitter3@0.1.6
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd -&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That’ll basically install the eventemitter3 NodeJS package module locally to the &lt;em&gt;grunt-connect-proxy&lt;/em&gt; module so to overcome the compatibility issues which in turn causes the error mentioned above.&lt;/p&gt;
&lt;p&gt;You should use your favourite editor to change the file &lt;em&gt;config.json&lt;/em&gt;, which basically contains all your local needed configuration. In particular our host is not exposed on the I2P or Tor network, so we &amp;ldquo;visually&amp;rdquo; disabled those options.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# lines with &amp;#34;+&amp;#34; needs to be replace the ones starting with a &amp;#34;-&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-  &amp;#34;name&amp;#34;: &amp;#34;img.bi&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+  &amp;#34;name&amp;#34;: &amp;#34;img.bi - End Point image sharing service&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-  &amp;#34;maxSize&amp;#34;: &amp;#34;3145728&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+  &amp;#34;maxSize&amp;#34;: &amp;#34;32145728&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-  &amp;#34;clearnet&amp;#34;: &amp;#34;https://img.bi&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+  &amp;#34;clearnet&amp;#34;: &amp;#34;https://imgbi.example&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-  &amp;#34;i2p&amp;#34;: &amp;#34;http://imgbi.i2p&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+  &amp;#34;i2p&amp;#34;: &amp;#34;http://NOTAVAILABLE.i2p&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-  &amp;#34;tor&amp;#34;: &amp;#34;http://imgbifwwqoixh7te.onion&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+  &amp;#34;tor&amp;#34;: &amp;#34;http://NOTAVAILABLE.onion&amp;#34;,&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Save and close the file. At this point you should be able to run “grunt” to build the project but if it fails on the multiresize task, just run&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;grunt --force&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;to ignore the warnings.&lt;/p&gt;
&lt;p&gt;That’s about everything you need for the &lt;em&gt;frontend&lt;/em&gt; part, so it’s now time to take care of the API.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git clone https://github.com/imgbi/img.bi-api.git
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd /home/imgbi/img.bi-api/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You now need to edit the two Python files which are the core of the API.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# edit code.py expired.py
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-upload_dir = &amp;#39;/home/img.bi/img.bi-files&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+upload_dir = &amp;#39;/home/imgbi/img.bi-files&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Verify that you’re not having any Python import related error, due to missing modules or else, by running the Python code.py file directly.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./code.py&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If that’s working okay, just create a symlink in the build directory in order to have the API created files available to the frontend&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ln -s /home/imgbi/img.bi-files /home/imgbi/img.bi/build/download&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then it’s time to spawn the actual Python daemon:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;spawn-fcgi -f /home/imgbi/img.bi-api/code.py -a 127.0.0.1 -p 1234&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The expired.py file is used by a cronjob which periodically checks if there’s any image/content that should be removed because its time has expired. First of all let’s call the script directly and if there’s no error, let’s create the crontab:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python /home/imgbi/img.bi-api/expired.py
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;crontab -e
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@reboot spawn-fcgi -f /home/imgbi/img.bi-api/code.py -a 127.0.0.1 -p 1234
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;30 4 * * * python /home/imgbi/img.bi-api/expired.py&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It’s now time to install nginx and Redis (if you still haven’t done so), and then configure them. For Redis you can just follow the usual simple, basic installation and that’ll be just okay. Same is true for nginx but we’ll add our configuration/vhost file content here as an example /etc/nginx/sites-enabled/imgbi.example.conf for everyone who may need it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;upstream imgbi-fastcgi {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  server 127.0.0.1:1234;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;server {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  listen 80;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  listen [::]:80;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  server_name imgbi.example;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  access_log /var/log/nginx/sites/imgbi.example/access.log;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  error_log /var/log/nginx/sites/imgbi.example/error.log;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  rewrite ^ https://imgbi.example/ permanent;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;server {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  listen 443 ssl spdy;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  listen [::]:443 ssl spdy;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  server_name  imgbi.example;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  server_name  imgbi.example;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  access_log /var/log/nginx/sites/imgbi.example/access.log;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  error_log /var/log/nginx/sites/imgbi.example/error.log;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  client_max_body_size 4G;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  include include/ssl-wildcard-example.inc;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  add_header Strict-Transport-Security max-age=31536000;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  add_header X-Frame-Options SAMEORIGIN;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  add_header X-Content-Type-Options nosniff;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  add_header X-XSS-Protection &amp;#34;1; mode=block&amp;#34;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  location / {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    root /home/imgbi/img.bi/build;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  location /api {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fastcgi_param QUERY_STRING $query_string;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fastcgi_param REQUEST_METHOD $request_method;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fastcgi_param CONTENT_TYPE $content_type;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fastcgi_param CONTENT_LENGTH $content_length;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fastcgi_param SCRIPT_NAME &amp;#34;&amp;#34;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fastcgi_param PATH_INFO $uri;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fastcgi_param REQUEST_URI $request_uri;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fastcgi_param DOCUMENT_URI $document_uri;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fastcgi_param DOCUMENT_ROOT $document_root;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fastcgi_param SERVER_PROTOCOL $server_protocol;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fastcgi_param GATEWAY_INTERFACE CGI/1.1;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fastcgi_param REMOTE_ADDR $remote_addr;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fastcgi_param REMOTE_PORT $remote_port;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fastcgi_param SERVER_ADDR $server_addr;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fastcgi_param SERVER_PORT $server_port;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fastcgi_param SERVER_NAME $server_name;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fastcgi_param HTTPS on;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fastcgi_pass imgbi-fastcgi;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fastcgi_keep_conn on;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Well, that should be enough to get you started and at least have all the components in place. Enjoy your secure image sharing now.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Web Development, Big Data and DevOps—​OSI Days 2014, India</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/01/web-development-big-data-and-devops-osi/"/>
      <id>https://www.endpointdev.com/blog/2015/01/web-development-big-data-and-devops-osi/</id>
      <published>2015-01-12T00:00:00+00:00</published>
      <author>
        <name>Selvakumar Arumugam</name>
      </author>
      <content type="html">
        &lt;p&gt;This is the second part of an article about the conference &lt;a href=&#34;https://opensourceindia.in/osidays/&#34;&gt;Open Source India&lt;/a&gt;, 2014 was held at Bengaluru, India. The first part is available &lt;a href=&#34;/blog/2014/11/mongodb-and-openstack-osi-days-2014/&#34;&gt;here&lt;/a&gt;. The second day of the conference started with the same excitement level. I plan to attend talks covering Web, Big Data, Logs monitoring and Docker.&lt;/p&gt;
&lt;h3 id=&#34;web-personalisation&#34;&gt;Web Personalisation&lt;/h3&gt;
&lt;p&gt;Jacob Singh started the first talk session with a wonderful presentation along with real-world cases which explained the importance of personalisation in the web. It extended to content personalisation for users and A/B testing (comparing two versions of a webpage to see which one performs better). The demo used the &lt;a href=&#34;https://www.drupal.org/project/acquia_lift&#34;&gt;Acquia Lift&lt;/a&gt; personalisation module for the &lt;a href=&#34;https://www.drupal.org/&#34;&gt;Drupal&lt;/a&gt; CMS which is developed by his team.&lt;/p&gt;
&lt;h3 id=&#34;mean-stack&#34;&gt;MEAN Stack&lt;/h3&gt;
&lt;p&gt;Sateesh Kavuri of Yodlee spoke about the &lt;a href=&#34;http://mean.io/&#34;&gt;MEAN&lt;/a&gt; stack which is a web development stack equivalent to popular LAMP stack. MEAN provides a flexible compatibility to web and mobile applications. He explained the architecture of MEAN stack.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2015/01/web-development-big-data-and-devops-osi/image-0.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; height=&#34;480&#34; src=&#34;/blog/2015/01/web-development-big-data-and-devops-osi/image-0.png&#34; width=&#34;640&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;He also provided an overview of each component involved in MEAN Stack.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.mongodb.org/&#34;&gt;MongoDB&lt;/a&gt; — NoSQL database with dynamic schema, in-built aggregation, mapreduce, JSON style document, auto-sharding, extensive query mechanism and high availability.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://expressjs.com/&#34;&gt;ExpressJS&lt;/a&gt; — A node.js framework to provide features to web and mobile applications.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://angularjs.org/&#34;&gt;AngularJS&lt;/a&gt; — seamless bi-directional model with extensive features like services and directives.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://nodejs.org/&#34;&gt;Node.js&lt;/a&gt; — A server side JavaScript framework with event based programming and single threaded (non blocking I/O with help of request queue).&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://sailsjs.org/&#34;&gt;Sails.js&lt;/a&gt; — MEAN Stack provisioner to develop applications quickly.&lt;/p&gt;
&lt;p&gt;Finally he demonstrated a MEAN Stack demo application provisioned with help of Sails.js.&lt;/p&gt;
&lt;h3 id=&#34;moving-fast-with-high-performance-hack-and-php&#34;&gt;Moving fast with high performance Hack and PHP&lt;/h3&gt;
&lt;p&gt;Dushyant Min spoke about the way Facebook optimised the PHP code base to deliver better performance when they supposed to handle a massive growth of users. Earlier there were compilers HipHop for PHP (HPHPc) or HPHPi (developer mode) to convert the php code to C++ binary and executed to provide the response. After sometime, Facebook developed a new compilation engine called &lt;a href=&#34;https://hhvm.com/&#34;&gt;HipHop Virtual Machine&lt;/a&gt; which uses Just-In-Time (JIT) compilation approach and converts the code to HipHop ByteCode (HHBC). Both Facebook’s production and development environment code runs over HHVM.&lt;/p&gt;
&lt;p&gt;Facebook also created a new language called &lt;a href=&#34;http://hacklang.org/&#34;&gt;Hack&lt;/a&gt; which is very similar to PHP which added static typing and many other new features. The main reason for Hack is to get the fastest development cycle to add new features and release frequent versions. Hack also uses the HHVM engine.&lt;/p&gt;
&lt;p&gt;HHVM engine supports both PHP and Hack, also it provides better performance compare to Zend engine. So Zend Engine can be replaced with HHVM without any issues in the existing PHP applications to get much better performance. It is simple as below:&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2015/01/web-development-big-data-and-devops-osi/image-1.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; height=&#34;480&#34; src=&#34;/blog/2015/01/web-development-big-data-and-devops-osi/image-1.png&#34; width=&#34;640&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Also PHP code can be migrated to Hack by changing the &lt;code&gt;&amp;lt;?php&lt;/code&gt; tag to &lt;code&gt;&amp;lt;?hh&lt;/code&gt; and there are some converters (hackficator) available for code migration. Both PHP and Hack provide almost the same performance on the HHVM engine, but Hack has some additional developer-focussed features.&lt;/p&gt;
&lt;h3 id=&#34;application-monitoring-and-log-management&#34;&gt;Application Monitoring and Log Management&lt;/h3&gt;
&lt;p&gt;Abhishek Dwivedi spoke about a stack to process the logs with various formats, myriad timestamp and no context. He explains a stack of tools to process the logs, store and visualize in a elegant way.&lt;/p&gt;
&lt;p&gt;ELK Stack = Elasticsearch, LogStash, Kibana. The architecture and the data flow of ELK stack is stated below:&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2015/01/web-development-big-data-and-devops-osi/image-2.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; height=&#34;438&#34; src=&#34;/blog/2015/01/web-development-big-data-and-devops-osi/image-2.png&#34; width=&#34;640&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&#34;https://www.elastic.co/&#34;&gt;Elasticsearch&lt;/a&gt; — Open source full text search and analytics engine&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.elastic.co/products/logstash&#34;&gt;Log Stash&lt;/a&gt; — Open source tool for managing events and logs which has following steps to process the logs&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.elastic.co/products/kibana&#34;&gt;Kibana&lt;/a&gt; — seamlessly works with Elasticsearch and provides elegant user interface with various types of graphs&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2015/01/web-development-big-data-and-devops-osi/image-3.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; height=&#34;138&#34; src=&#34;/blog/2015/01/web-development-big-data-and-devops-osi/image-3.png&#34; width=&#34;640&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h3 id=&#34;apache-spark&#34;&gt;Apache Spark&lt;/h3&gt;
&lt;p&gt;Prajod and Namitha presented the overview of Apache Spark which is a real time data processing system. It can work on top of Hadoop Distributed FileSystem (HDFS). &lt;a href=&#34;https://spark.apache.org/&#34;&gt;Apache Spark&lt;/a&gt; performs 100x faster in memory and 10x faster in disk compare to Hadoop. It fits with Streaming and Interactive scale of Big Data processing.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2015/01/web-development-big-data-and-devops-osi/image-4.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; height=&#34;480&#34; src=&#34;/blog/2015/01/web-development-big-data-and-devops-osi/image-4.png&#34; width=&#34;640&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Apache Spark has certain features in processing the data to deliver the promising performance:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Multistep Directed Acyclic Graph&lt;/li&gt;
&lt;li&gt;Cached Intermediate Data&lt;/li&gt;
&lt;li&gt;Resilient Distributed Data&lt;/li&gt;
&lt;li&gt;Spark Streaming — Adjust batch time to get the near real time data process&lt;/li&gt;
&lt;li&gt;Implementation of Lambda architecture&lt;/li&gt;
&lt;li&gt;Graphx and Mlib libraries play an important role&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;online-data-processing-in-twitter&#34;&gt;Online Data Processing in Twitter&lt;/h3&gt;
&lt;p&gt;Lohit Vijayarenu from Twitter spoke about the technologies used at Twitter and their contributions to Open Source. Also he explained the higher level architecture and technologies used in the Twitter microblogging social media platform.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2015/01/web-development-big-data-and-devops-osi/image-5.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; height=&#34;338&#34; src=&#34;/blog/2015/01/web-development-big-data-and-devops-osi/image-5.png&#34; width=&#34;640&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The Twitter front end is the main data input for the system. The Facebook-developed Scribe log servers gather the data from the Twitter front end application and transfers the data to both batch and real time Big Data processing systems. Storm is a real time data processing system which takes care of the happening events at the site. Hadoop is a batch processing system which runs over historical data and generates result data to perform analysis. Several high level abstraction tools like PIG are used write the MR jobs. Along with these frameworks and tools at the high level architecture, there are plenty of Open Source tools used in Twitter. Lohit also strongly mentioned that in addition to using Open Source tools, Twitter contributes back to Open Source.&lt;/p&gt;
&lt;h3 id=&#34;docker&#34;&gt;Docker&lt;/h3&gt;
&lt;p&gt;Neependra Khare from Red Hat given a talk and demo on &lt;a href=&#34;https://www.docker.com/&#34;&gt;Docker&lt;/a&gt; which was very interactive session. The gist of Docker is to build, ship and run any application anywhere. It provides good performance and resource utilization compared to the traditional VM model. It uses the Linux core feature called containerization. The container storage is ephemeral, so the important data can be stored in persistent external storage volumes. Slides can be found &lt;a href=&#34;https://github.com/nkhare/presetations/blob/master/osidays/osi_docker.md&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Liquid Galaxy and its Very Own Street View App</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2013/11/liquid-galaxy-and-its-very-own-street/"/>
      <id>https://www.endpointdev.com/blog/2013/11/liquid-galaxy-and-its-very-own-street/</id>
      <published>2013-11-09T00:00:00+00:00</published>
      <author>
        <name>Matt Vollrath</name>
      </author>
      <content type="html">
        &lt;h3 id=&#34;liquid-galaxy-does-street-view&#34;&gt;Liquid Galaxy does Street View!&lt;/h3&gt;
&lt;p&gt;Peruse-a-Rue is the combination of a Node.js server with a Maps API browser client, all wrapped up in one neat bundle. The result of this marriage is a highly compelling immersive Street View experience.&lt;/p&gt;
&lt;p&gt;Everything from a single screen kiosk to a cylindrical Liquid Galaxy to an endless display wall can be configured, with bezel offsets, portrait or landscape. A touchscreen control interface is optional, and a Space Navigator can drive the display.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2013/11/liquid-galaxy-and-its-very-own-street/image-0.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img alt=&#34;Testing Peruse-a-Rue&#34; border=&#34;0&#34; height=&#34;295&#34; src=&#34;/blog/2013/11/liquid-galaxy-and-its-very-own-street/image-0.png&#34; width=&#34;480&#34;/&gt;&lt;/a&gt;&lt;p style=&#34;text-align: center; font-size: 0.8em;&#34;&gt;Testing Peruse-a-Rue on the desktop&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;By leveraging the Connect framework for Node, the entire application is served on a single port. Any number of browser windows can be synchronized, thanks to the scalability of websockets. When integrated with the Squid caching of the Liquid Galaxy project, redundant downloading is eliminated; each screen shares retrieved tile data with its peers.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2013/11/liquid-galaxy-and-its-very-own-street/image-1.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img alt=&#34;Peruse-a-Rue Touchscreen&#34; border=&#34;0&#34; height=&#34;281&#34; src=&#34;/blog/2013/11/liquid-galaxy-and-its-very-own-street/image-1.png&#34; width=&#34;480&#34;/&gt;&lt;/a&gt;&lt;p style=&#34;text-align: center; font-size: 0.8em;&#34;&gt;The Peruse-a-Rue touchscreen interface&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;Since NPM installs dependencies automatically, deployment is a breeze. Every Liquid Galaxy is a git checkout and an &lt;code&gt;npm install&lt;/code&gt; away from running the server. Peruse-a-Rue supports any operating system that can run Node.js (as a server) or Google Chrome (as a client). I’ve even tested the server on a Raspberry Pi and BeagleBone Black, and it runs perfectly!&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2013/11/liquid-galaxy-and-its-very-own-street/image-2.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img alt=&#34;Raspberry Pi&#34; border=&#34;0&#34; height=&#34;247&#34; src=&#34;/blog/2013/11/liquid-galaxy-and-its-very-own-street/image-2.png&#34; width=&#34;480&#34;/&gt;&lt;/a&gt;&lt;p style=&#34;text-align: center; font-size: 0.8em;&#34;&gt;It runs on this thing, too&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;Peruse-a-Rue is hosted &lt;a href=&#34;https://github.com/EndPointCorp/lg-peruse-a-rue&#34;&gt;here&lt;/a&gt;. If you’re interested in the project or want to contribute, drop us a line.&lt;/p&gt;
&lt;p&gt;Happy Perusing!&lt;/p&gt;

      </content>
    </entry>
  
</feed>
