<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Brett Hoyer</title>
    <description>The latest articles on Forem by Brett Hoyer (@bretthoyer).</description>
    <link>https://forem.com/bretthoyer</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F883541%2F0edbda57-83ea-453b-8506-0d63906f14e5.png</url>
      <title>Forem: Brett Hoyer</title>
      <link>https://forem.com/bretthoyer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/bretthoyer"/>
    <language>en</language>
    <item>
      <title>Handling Automatic ID Generation in PostgreSQL with Node.js and Sequelize</title>
      <dc:creator>Brett Hoyer</dc:creator>
      <pubDate>Tue, 03 Jan 2023 20:22:01 +0000</pubDate>
      <link>https://forem.com/yugabyte/handling-automatic-id-generation-in-postgresql-with-nodejs-and-sequelize-2420</link>
      <guid>https://forem.com/yugabyte/handling-automatic-id-generation-in-postgresql-with-nodejs-and-sequelize-2420</guid>
      <description>&lt;p&gt;&lt;em&gt;Automatic ID generation for database records is a fundamental part of application development. In this article, I’ll demonstrate four ways to handle automatic ID generation in &lt;a href="https://sequelize.org/" rel="noopener noreferrer"&gt;Sequelize&lt;/a&gt; for &lt;a href="https://www.postgresql.org/" rel="noopener noreferrer"&gt;PostgreSQL&lt;/a&gt; and &lt;a href="https://www.yugabyte.com/yugabytedb/" rel="noopener noreferrer"&gt;YugabyteDB&lt;/a&gt;, the open source, cloud native,  distributed SQL database built on PostgreSQL.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There are many ways to handle ID generation in PostgreSQL, but I've chosen to investigate these approaches:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Auto-incrementing (SERIAL data type)&lt;/li&gt;
&lt;li&gt;Sequence-caching&lt;/li&gt;
&lt;li&gt;Sequence-incrementing with client-side ID management&lt;/li&gt;
&lt;li&gt;UUID-generation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Depending on your application and your underlying database tables, you might choose to employ one or more of these options. Below I'll explain how each can be achieved in Node.js using the &lt;a href="https://sequelize.org/docs/v6/" rel="noopener noreferrer"&gt;Sequelize&lt;/a&gt; ORM.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Auto-Incrementing
&lt;/h2&gt;

&lt;p&gt;Most developers choose the most straightforward option before exploring potential optimizations. I'm no different! Here's how you can create an auto-incrementing ID field in your Sequelize model definitions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Sequelize
const { DataTypes } = require('sequelize');
const Product = sequelize.define(
   "product",
   {
       id: {
           type: DataTypes.INTEGER,
           autoIncrement: true,
           primaryKey: true,
       },
       title: {
           type: DataTypes.STRING,
       }
   }
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you’re familiar with Sequelize, you’ll be no stranger to this syntax, but others might wonder what's actually happening under the hood.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;autoIncrement&lt;/code&gt; flag tells PostgreSQL to create an &lt;code&gt;id&lt;/code&gt; column with a &lt;a href="https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-SERIAL" rel="noopener noreferrer"&gt;SERIAL&lt;/a&gt; data type. This data type implicitly creates a &lt;a href="https://www.postgresql.org/docs/current/sql-createsequence.html" rel="noopener noreferrer"&gt;SEQUENCE&lt;/a&gt; which is owned by the &lt;code&gt;products&lt;/code&gt; table's &lt;code&gt;id&lt;/code&gt; column.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// PostgreSQL equivalent
CREATE SEQUENCE products_id_seq;
CREATE TABLE products
(
   id INT NOT NULL DEFAULT NEXTVAL('products_id_seq'),
   title VARCHAR(255)
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When inserting a product into our table, we don't need to supply a value for &lt;code&gt;id&lt;/code&gt;, as it's automatically-generated from the underlying sequence.&lt;/p&gt;

&lt;p&gt;We can simply run the following to insert a product.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Sequelize
await Product.create({title: "iPad Pro"});


//PostgreSQL equivalent
INSERT INTO products (title) VALUES ('iPad Pro');

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

&lt;/div&gt;



&lt;p&gt;Dropping our table will also drop the automatically-created sequence, &lt;code&gt;products_id_seq&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Sequelize
await Product.drop();

// PostgreSQL equivalent
DROP TABLE products CASCADE;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although this approach is extremely easy to implement, our PostgreSQL server needs to access the sequence to get its next value on every write, which comes at a latency cost. This is particularly bad in distributed deployments. YugabyteDB sets a &lt;a href="https://docs.yugabyte.com/preview/reference/configuration/yb-tserver/#ysql-sequence-cache-minval" rel="noopener noreferrer"&gt;default&lt;/a&gt; sequence cache size of 100. I’ll outline why this is so important below.&lt;/p&gt;

&lt;p&gt;Now that we have the basics out of the way, let's try to speed things up. As we all know, "cache is king."&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Sequence-Caching
&lt;/h2&gt;

&lt;p&gt;Although the &lt;code&gt;autoIncrement&lt;/code&gt; flag in Sequelize model definitions totally eliminates the need to interact with sequences directly, there are scenarios where you might consider doing so. For instance, what if you wanted to speed up writes by caching sequence values? Fear not, with a little extra effort, we can make this happen.&lt;/p&gt;

&lt;p&gt;Sequelize doesn't have API support to make this happen, as noted on Github (&lt;a href="https://github.com/sequelize/sequelize/issues/3555#issuecomment-1132630072" rel="noopener noreferrer"&gt;https://github.com/sequelize/sequelize/issues/3555#issuecomment-1132630072&lt;/a&gt;), but there's a simple workaround. By utilizing the built-in &lt;code&gt;literal&lt;/code&gt; function, we are able to access a predefined sequence in our model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { literal, DataTypes } = require('sequelize');
const Product = sequelize.define("product", {
 id: {
   type: DataTypes.INTEGER,
   primaryKey: true,
   defaultValue: literal("nextval('custom_sequence')"),
 },
});

sequelize.beforeSync(() =&amp;gt; {
 await sequelize.query('CREATE SEQUENCE IF NOT EXISTS custom_sequence CACHE 50');
});

await sequelize.sync();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's not too bad. So, this is what changed:&lt;br&gt;
We've created our own sequence, named &lt;code&gt;custom_sequence&lt;/code&gt;, which is used to set the default value for our product ID. &lt;br&gt;
This sequence is created in the beforeSync hook, so it will be created before the products table and its &lt;code&gt;CACHE&lt;/code&gt; value has been set to 50.&lt;br&gt;
The &lt;code&gt;defaultValue&lt;/code&gt; is set to the next value in our custom sequence.&lt;/p&gt;

&lt;p&gt;Well, what about the cache? Sequences in PostgreSQL can optionally be supplied a &lt;code&gt;CACHE&lt;/code&gt; value upon creation, which allots a certain number of values to be stored in memory per session. With our cache set at 50, here's how that works.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//Database Session A
&amp;gt; SELECT nextval('custom_sequence');
1
&amp;gt; SELECT nextval('custom_sequence');
2

//Database Session B
&amp;gt; SELECT nextval('custom_sequence');
51
&amp;gt;
52
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For an application with multiple database connections, such as one running microservices or multiple servers behind a load balancer, each connection will receive a set of cached values. No session will contain duplicate values in its cache, ensuring there are no collisions when inserting records. In fact, depending on how your database is configured, you might even find &lt;a href="https://www.cybertec-postgresql.com/en/gaps-in-sequences-postgresql/" rel="noopener noreferrer"&gt;gaps&lt;/a&gt; in your sequenced &lt;code&gt;id&lt;/code&gt; column if a database connection fails and is restarted without using all of the values alloted in its cache. However, this generally isn't a problem, as we're only concerned with uniqueness.&lt;/p&gt;

&lt;p&gt;So, what's the point? Speed. Speed is the point!&lt;/p&gt;

&lt;p&gt;By caching values on our PostgreSQL backend and storing them in memory, we're able to retrieve the next value very quickly. In fact, YugabyteDB caches &lt;a href="https://stackoverflow.com/a/70740723" rel="noopener noreferrer"&gt;100 sequence values by default&lt;/a&gt;, as opposed to the PostgreSQL default of 1. This allows the database to scale, without needing to repeatedly obtain the next sequence value from the master node on writes. Of course, caching comes with the drawback of an increased memory constraint on the PostgreSQL server.&lt;/p&gt;

&lt;p&gt;Depending on your infrastructure, this could be a worthy optimization!&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Client-Side Sequencing
&lt;/h2&gt;

&lt;p&gt;Sequence-caching improves performance by caching values on our PostgreSQL backend. How could we use a sequence to cache values on our client instead?&lt;/p&gt;

&lt;p&gt;Sequences in PostgreSQL have an additional parameter called &lt;code&gt;INCREMENT BY&lt;/code&gt; that can be used to achieve this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// DB Initialization
const { literal, DataTypes } = require('sequelize');
const Product = sequelize.define("product", {
 id: {
   type: DataTypes.INTEGER,
   primaryKey: true
 },
});

sequelize.beforeSync(() =&amp;gt; {
 await sequelize.query('CREATE SEQUENCE IF NOT EXISTS custom_sequence INCREMENT BY 50');
});

await sequelize.sync();

// Caller
let startVal = await sequelize.query("SELECT nextval('custom_sequence')");
let limit = startVal + 50;

if (startVal &amp;gt;= limit) {
   startVal = await sequelize.query("SELECT nextval('custom_sequence')");
   limit = startVal + 50;
}

await Product.create({id: startVal, title: "iPad Pro"})
startVal += 1;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we're utilizing our custom sequence in a slightly different way. No default value is supplied to our model definition. Instead, we're using this sequence to set unique values client-side, by looping through the values in the increment range. When we've exhausted all of the values in this range, we make another call to our database to get the next value in our sequence to "refresh" our range.&lt;/p&gt;

&lt;p&gt;Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Database Session A

&amp;gt; SELECT nextval('custom_sequence');
1

*
 inserts 50 records
 // id 1
 // id 2
 ...
 // id 50
*

&amp;gt; SELECT nextval('custom_sequence');
151

// Database Session B

&amp;gt; SELECT nextval('custom_sequence');
51

* inserts 50 records before Session A has used all numbers in its range *

&amp;gt; SELECT nextval('custom_sequence');
101
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Database Session A&lt;/em&gt; connects and receives the first value in the sequence. &lt;em&gt;Database Session B&lt;/em&gt; connects and receives the value of 51 because we've set our &lt;code&gt;INCREMENT BY&lt;/code&gt; value to &lt;code&gt;50&lt;/code&gt;. Like our auto-incrementing solutions, we can ensure that there are no ID collisions by referencing our PostgreSQL sequence to determine the start value for our range.&lt;/p&gt;

&lt;p&gt;What problems might arise from this solution? Well, it's possible that a database administrator could choose to increase or decrease the &lt;code&gt;INCREMENT BY&lt;/code&gt; value for a particular sequence, without application developers being notified of this change. This would break application logic.&lt;/p&gt;

&lt;p&gt;How can we benefit from client-side sequencing? If you have a lot of available memory on your application server nodes, this could be a potential performance benefit over sequence-caching on database nodes. &lt;/p&gt;

&lt;p&gt;In fact, you might be wondering if it’s possible to utilize a cache on the client and server in the same implementation. The short answer is YES. By creating a sequence with &lt;code&gt;CACHE&lt;/code&gt; and &lt;code&gt;INCREMENT BY&lt;/code&gt; values, we benefit from a server-side cache of our sequence values and a client-side cache for the next value in our range. This performance optimization provides the best of both worlds if memory constraints are not of primary concern.&lt;/p&gt;

&lt;p&gt;Enough with the sequences already! Let's move on to unique identifiers.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. UUID-Generation
&lt;/h2&gt;

&lt;p&gt;We've covered three ways to generate sequential, integer-based IDs. Another data type, the Universally Unique Identifier (&lt;a href="https://en.wikipedia.org/wiki/Universally_unique_identifier" rel="noopener noreferrer"&gt;UUID&lt;/a&gt;), removes the need for sequences entirely.&lt;/p&gt;

&lt;p&gt;A UUID is a 128-bit identifier, which comes with the guarantee of uniqueness due to the incredibly small probability that the same ID would be generated twice.&lt;/p&gt;

&lt;p&gt;PostgreSQL comes with an extension called &lt;a href="https://www.postgresql.org/docs/current/pgcrypto.html" rel="noopener noreferrer"&gt;pgcrypto&lt;/a&gt; (also supported by YugabyteDB), which can be installed to generate UUIDs with the &lt;a href="https://www.postgresql.org/docs/current/functions-uuid.html" rel="noopener noreferrer"&gt;gen_random_uuid&lt;/a&gt; function. This function generates a UUID value for a database column, much the same that &lt;code&gt;nextval&lt;/code&gt; is used with sequences.&lt;/p&gt;

&lt;p&gt;Additionally, Node.js has several packages which generate UUIDs, such as, you guessed it, &lt;a href="https://www.npmjs.com/package/uuid" rel="noopener noreferrer"&gt;uuid&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Sequelize
const { literal, DataTypes } = require('sequelize');
const Product = sequelize.define(
   "product",
   {
       id: {
           type: DataTypes.UUID,
           defaultValue: literal('gen_random_uuid()')
           primaryKey: true,
       },
       title: {
           type: DataTypes.STRING,
       }
   }
);

sequelize.beforeSync(() =&amp;gt; {
 await sequelize.query('CREATE EXTENSION IF NOT EXISTS "pgcrypto"');
});


// PostreSQL Equivalent
CREATE TABLE products
(
   id UUID NOT NULL DEFAULT gen_random_uuid(),
   title VARCHAR(255)
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows us to generate a UUID client-side, with a server-side default, if required.&lt;/p&gt;

&lt;p&gt;A UUID-based approach brings unique benefits with the random nature of the data type being helpful with certain data migrations. This is also helpful for API security, as the unique identifier is in no way tied to the information being stored.&lt;/p&gt;

&lt;p&gt;Additionally, the ability to generate an ID client side without managing state is helpful in a distributed deployment, where network latencies play a big role in application performance.&lt;/p&gt;

&lt;p&gt;For example, in a &lt;a href="https://docs.yugabyte.com/preview/explore/multi-region-deployments/row-level-geo-partitioning/" rel="noopener noreferrer"&gt;geo-partioned&lt;/a&gt; YugabyteDB cluster, connections are made to the nearest database node to serve low-latency reads. However, on writes, this node must forward the request to the primary node in the cluster (which could reside in another region of the world) to determine the next sequence value. The use of UUIDs eliminates this traffic, providing a performance boost.&lt;/p&gt;

&lt;p&gt;So, what's the downside? Well, the topic of UUIDs is somewhat &lt;a href="https://www.depesz.com/2020/02/19/why-im-not-fan-of-uuid-datatype/" rel="noopener noreferrer"&gt;polarizing&lt;/a&gt;. One obvious downside would be the storage size of a UUID relative to an integer, 16 bytes as opposed to 4 bytes for an &lt;code&gt;INTEGER&lt;/code&gt; and 8 for a &lt;code&gt;BIGINT&lt;/code&gt;. UUIDs also take some time to generate, which is a performance consideration. &lt;/p&gt;

&lt;p&gt;Some of the concerns regarding using UUIDs as primary keys are illustrated in this &lt;a href="https://vladmihalcea.com/uuid-database-primary-key" rel="noopener noreferrer"&gt;post&lt;/a&gt; and are discussed further with regards to YugabyteDB in this &lt;a href="https://twitter.com/vlad_mihalcea/status/1600739305055735808?s=20&amp;amp;t=ph7CKNyjSAcS_cZJPLIWTQ" rel="noopener noreferrer"&gt;thread&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can read more about the tradeoffs between integer and UUID based IDs &lt;a href="https://dev.to/yugabyte/uuid-or-cached-sequences-42fi"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Building
&lt;/h2&gt;

&lt;p&gt;Ultimately, there are many factors to consider when choosing how to generate your database IDs. You might choose to use auto-incrementing IDs for a table with infrequent writes, or one that doesn't require low-latency writes. Another table, spread across multiple geographies in a multi-node deployment, might benefit from using UUIDs. There's only one way to find out. Get out there and write some code!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you're interested in using an always-free, PostgreSQL-compatible database node, give &lt;a href="https://www.yugabyte.com/managed/" rel="noopener noreferrer"&gt;YugabyteDB Managed&lt;/a&gt; a try.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>emptystring</category>
    </item>
    <item>
      <title>Bulk Loading Data In PostgreSQL With Node.js and Sequelize</title>
      <dc:creator>Brett Hoyer</dc:creator>
      <pubDate>Tue, 27 Dec 2022 18:17:56 +0000</pubDate>
      <link>https://forem.com/yugabyte/bulk-loading-data-in-postgresql-with-nodejs-and-sequelize-1bn7</link>
      <guid>https://forem.com/yugabyte/bulk-loading-data-in-postgresql-with-nodejs-and-sequelize-1bn7</guid>
      <description>&lt;p&gt;&lt;em&gt;Application development often requires seeding data in a database for testing and development. The following article will outline how to handle this using Node.js and Sequelize.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Whether you're building an application from scratch with zero users, or adding features to an existing application, working with data during development is a necessity. This can take different forms, from mock data APIs reading data files in development, to seeded database deployments closely mirroring an expected production environment.&lt;/p&gt;

&lt;p&gt;I prefer the latter as I find fewer deviations from my production toolset leads to fewer bugs.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Humble Beginning
&lt;/h2&gt;

&lt;p&gt;For the sake of this discussion, let's assume we're building an online learning platform offering various coding courses. In its simplest form, our Node.js API layer might look like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// server.js

const express = require("express");
const App = express();

const courses = [
   {title: "CSS Fundamentals", "thumbnail": "https://fake-url.com/css"}],
   {title: "JavaScript Basics", "thumbnail": "https://fake-url.com/js-basics"}],
   {title: "Intermediate JavaScript", "thumbnail": "https://fake-url.com/intermediate-js"}
];

App.get("/courses", (req, res) =&amp;gt; {
   res.json({data: courses});
});

App.listen(3000);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If all you need is a few items to start building your UI, this is enough to get going. Making a call to our &lt;code&gt;/courses&lt;/code&gt; endpoint will return all of the courses defined in this file. However, what if we want to begin testing with a dataset more representative of a full-fledged database-backed application?&lt;/p&gt;

&lt;h2&gt;
  
  
  Working With JSON
&lt;/h2&gt;

&lt;p&gt;Suppose we inherited a script exporting a JSON-array containing thousands of courses. We could import the data, like so.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// courses.js

module.exports = [
   {title: "CSS Fundamentals", "thumbnail": "https://fake-url.com/css"}],
   {title: "JavaScript Basics", "thumbnail": "https://fake-url.com/js-basics"}],
   {title: "Intermediate JavaScript", "thumbnail": "https://fake-url.com/intermediate-js"},
   ...
];

// server.js

...
const courses = require("/path/to/courses.js");
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This eliminates the need to define our mock data within our server file, and now we have plenty of data to work with. We could enhance our endpoint by adding parameters to paginate the results and set limits on how many records are returned. But, what about allowing users to post their own courses? How about editing courses?&lt;/p&gt;

&lt;p&gt;This solution gets out of hand quickly as you begin to add functionality. We'll have to write additional code to simulate features of a relational database. After all, databases were created to store data. So, let's do that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bulk Loading JSON With Sequelize
&lt;/h2&gt;

&lt;p&gt;For an application of this nature, PostgreSQL is an appropriate database selection. We have the option of &lt;a href="https://www.postgresql.org/download/" rel="noopener noreferrer"&gt;running PostgreSQL locally&lt;/a&gt;, or connecting to a PostgreSQL-compatible cloud native database, like &lt;a href="https://www.yugabyte.com/managed/" rel="noopener noreferrer"&gt;YugabyteDB Managed&lt;/a&gt;. Apart from being a highly-performant distributed SQL database, developers using YugabyteDB benefit from a cluster that can be shared by multiple users. As the application grows, our data layer can scale out to multiple nodes and regions.&lt;/p&gt;

&lt;p&gt;After creating a YugabyteDB Managed account and spinning up a free database cluster, we're ready to seed our database and refactor our code, using &lt;a href="https://sequelize.org/" rel="noopener noreferrer"&gt;Sequelize&lt;/a&gt;. The Sequelize ORM allows us to model our data to create database tables and execute commands. Here's how that works.&lt;/p&gt;

&lt;p&gt;First, we install Sequelize from our terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// terminal
&amp;gt; npm i sequelize
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we use Sequelize to establish a connection to our database, create a table, and seed our table with data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// database.js

// JSON-array of courses
const courses = require("/path/to/courses.js");

// Certificate file downloaded from YugabyteDB Managed
const cert = fs.readFileSync(CERTIFICATE_PATH).toString();

// Create a Sequelize instance with our database connection details
const Sequelize = require("sequelize");
const sequelize = new Sequelize("yugabyte", "admin", DB_PASSWORD, {
   host: DB_HOST,
   port: "5433",
   dialect: "postgres",
   dialectOptions: {
   ssl: {
       require: true,
       rejectUnauthorized: true,
       ca: cert,
   },
   },
   pool: {
   max: 5,
   min: 1,
   acquire: 30000,
   idle: 10000,
   }
});

// Defining our Course model
export const Course = sequelize.define(
   "course",
   {
       id: {
           type: DataTypes.INTEGER,
           autoIncrement: true,
           primaryKey: true,
       },
       title: {
           type: DataTypes.STRING,
       },

       thumbnail: {
           type: DataTypes.STRING,
       },
   }
);


async function seedDatabase() {
   try {
       // Verify that database connection is valid
       await sequelize.authenticate();

       // Create database tables based on the models we've defined
       // Drops existing tables if there are any
       await sequelize.sync({ force: true });

       // Creates course records in bulk from our JSON-array
       await Course.bulkCreate(courses);

       console.log("Courses created successfully!");
   } catch(e) {
       console.log(`Error in seeding database with courses: ${e}`);
   }
}

// Running our seeding function
seedDatabase();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By leveraging Sequelize’s &lt;a href="https://sequelize.org/api/v6/class/src/model.js~model#static-method-bulkCreate" rel="noopener noreferrer"&gt;bulkCreate&lt;/a&gt; method, we’re able to insert multiple records in one statement. This is more performant than inserting requests one at a time, like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;. . .
// JSON-array of courses
const courses = require("/path/to/courses.js");

async function insertCourses(){
    for(let i = 0; i &amp;lt; courses.length; i++) {
    await Course.create(courses[i]); 
}
}

insertCourses();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Individual inserts come with the overhead of connecting, sending requests, parsing requests, indexing, closing connections, etc. on a one-off basis. Of course, some of these concerns are mitigated by connection pooling, but generally speaking the performance benefits of inserting in bulk are immense, not to mention far more convenient. The bulkCreate method even comes with a benchmarking option to pass query execution times to your logging functions, should performance be of primary concern.&lt;/p&gt;

&lt;p&gt;Now that our database is seeded with records, our API layer can use this Sequelize model to query the database and return courses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// server.js

const express = require("express");
const App = express();

// Course model exported from database.js
const { Course } = require("/path/to/database.js")

App.get("/courses", async (req, res) =&amp;gt; {
   try {
       const courses = await Course.findAll();
       res.json({data: courses});
   } catch(e) {
       console.log(`Error in courses endpoint: ${e}`);
   }
});
App.listen(3000);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, that was easy! We've moved from a static data structure to a fully-functioned database in no time.&lt;/p&gt;

&lt;p&gt;What if we're provided the dataset in another data format, say, a CSV file exported from Microsoft Excel? How can we use it to seed our database?&lt;/p&gt;

&lt;h2&gt;
  
  
  Working With CSVs
&lt;/h2&gt;

&lt;p&gt;There are many NPM packages to convert CSV files to JSON, but none are quite as easy to use as &lt;a href="https://www.npmjs.com/package/csvtojson" rel="noopener noreferrer"&gt;csvtojson&lt;/a&gt;. Start by installing the package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// terminal
&amp;gt; npm i csvtojson
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we use this package to convert our CSV file to a JSON-array, which can be used by Sequelize.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// courses.csv
title,thumbnail
CSS Fundamentals,https://fake-url.com/css
JavaScript Basics,https://fake-url.com/js-basics
Intermediate JavaScript,https://fake-url.com/intermediate-js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// database.js
...
const csv = require('csvtojson');
const csvFilePath = "/path/to/courses.csv";

// JSON-array of courses from CSV
const courses = await csv().fromFile(csvFilePath);
...
await Course.bulkCreate(courses);
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just as with our well-formatted &lt;code&gt;courses.js&lt;/code&gt; file, we're able to easily convert our &lt;code&gt;courses.csv&lt;/code&gt; file to bulk insert records via Sequelize.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Developing applications with hardcoded data can only take us so far. I find that investing in tooling early in the development process sets me on the path towards bug-free coding (or so I hope!) &lt;/p&gt;

&lt;p&gt;By bulk loading records, we’re able to work with a representative dataset, in a representative application environment. As I’m sure many agree, that’s often a major bottleneck in the application development process.&lt;/p&gt;

&lt;p&gt;Give Sequelize and &lt;a href="https://cloud.yugabyte.com/signup" rel="noopener noreferrer"&gt;YugabyteDB&lt;/a&gt; a try in your next Node.js coding adventure!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>postgres</category>
      <category>mysql</category>
    </item>
    <item>
      <title>How to Improve Node.js Application Latency Using Different Distributed SQL Deployments</title>
      <dc:creator>Brett Hoyer</dc:creator>
      <pubDate>Thu, 03 Nov 2022 18:49:09 +0000</pubDate>
      <link>https://forem.com/yugabyte/how-to-improve-nodejs-application-latency-using-different-distributed-sql-deployments-36c0</link>
      <guid>https://forem.com/yugabyte/how-to-improve-nodejs-application-latency-using-different-distributed-sql-deployments-36c0</guid>
      <description>&lt;p&gt;&lt;em&gt;Let’s improve  Node.js application latency using different YugabyteDB distributed SQL database configurations.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Application developers rely on different database configurations (and sometimes different databases altogether) to improve latency. &lt;a href="https://dev.to/bretthoyer/series/19070"&gt;The Largest River&lt;/a&gt;, my first distributed web application, is no different. &lt;/p&gt;

&lt;p&gt;From the start of this project, I've set out to explore how different &lt;a href="https://www.yugabyte.com/yugabytedb" rel="noopener noreferrer"&gt;YugabyteDB&lt;/a&gt; configurations could be deployed to improve latency for users across the globe. During this process, I've deployed three databases with various configurations in &lt;a href="https://www.yugabyte.com/managed/" rel="noopener noreferrer"&gt;YugabyteDB Managed&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is how it went….&lt;/p&gt;

&lt;h2&gt;
  
  
  A Simple Beginning
&lt;/h2&gt;

&lt;p&gt;Initially, I deployed a single-region, multi-zone cluster in &lt;a href="https://cloud.google.com/" rel="noopener noreferrer"&gt;Google Cloud&lt;/a&gt; in the &lt;code&gt;us-west2&lt;/code&gt; cloud region. &lt;/p&gt;

&lt;p&gt;Considering that databases have traditionally scaled vertically (adding more compute power to existing nodes) rather than horizontally (adding more instances), this basic &lt;a href="https://www.yugabyte.com/tech/distributed-sql/" rel="noopener noreferrer"&gt;distributed SQL&lt;/a&gt; configuration already had some benefits over many standard offerings. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs5t76i5bkhlaucp3zzpc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs5t76i5bkhlaucp3zzpc.png" alt="Single-region, multi-zone" width="800" height="538"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While I'm optimizing for latency, it is worth noting that a multi-zone deployment safeguards against outages in a particular data center. If one of our nodes failed, our YugabyteDB cluster would continue to operate by serving requests from the remaining nodes in the cluster.&lt;/p&gt;

&lt;p&gt;However, in terms of latency, this deployment does pose some issues. Users connecting from a nearby application instance will experience low latency (as little as &lt;code&gt;4ms&lt;/code&gt;), but those connecting from the other side of the globe, say in Australia, will suffer high latency (&lt;code&gt;250ms&lt;/code&gt; or more in some cases). &lt;/p&gt;

&lt;p&gt;This is often a reasonable tradeoff, but here we're building the next big global business (well, we aren't, but pretend we are!) and demand faster reads and writes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going Global
&lt;/h2&gt;

&lt;p&gt;Our friends on other continents are suffering. &lt;a href="https://www.yugabyte.com/blog/weathering-a-regional-cloud-outage-with-yugabytedb/" rel="noopener noreferrer"&gt;Multi-region&lt;/a&gt;, multi-zone with read replicas configuration to the rescue!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fejwshtkfyadj5ejrasa6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fejwshtkfyadj5ejrasa6.png" alt="Multi-region, multi-zone with read replicas" width="800" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this cluster deployment, we are putting our data closer to our end users. This drastically improves read latency. To illustrate, let’s use the same group in Sydney as an example. Their reads are now down to as little as &lt;code&gt;4ms&lt;/code&gt;, by connecting to the nearby read replica node. This is a major performance win on reads, but how about writes?&lt;/p&gt;

&lt;p&gt;However, writes from Sydney are still relatively slow in this deployment. While from a practical standpoint, our application is able to connect and send writes to the nearest replica node, this request is sent to the primary cluster to be committed. After all, a replica node is just that, a replica of the primary database. &lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.yugabyte.com/preview/architecture/docdb-replication/" rel="noopener noreferrer"&gt;DocDB replication layer&lt;/a&gt; has numerous options for replicating your data depending on your needs, so I suggest you explore further if going global is in your sights! &lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping Things Separate
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.yugabyte.com/preview/explore/multi-region-deployments/row-level-geo-partitioning/" rel="noopener noreferrer"&gt;Geo-partioned&lt;/a&gt; configurations can be used both to improve latency and uphold &lt;a href="https://www.yugabyte.com/blog/achieving-gdpr-compliance-with-yugabytedb/" rel="noopener noreferrer"&gt;compliance regulations&lt;/a&gt;. For instance, European Union and Indian regulations state that all user data collected in these territories must also be stored in these territories.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb2estde93clp7b5gbsti.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb2estde93clp7b5gbsti.png" alt="Geo-partitioned" width="800" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This configuration might also make sense for a global e-commerce application, as product catalogs might be different across geographies. Serving reads from these nodes is extremely fast, much like our previous multi-region deployment. In addition, writes are fast because we are able to commit writes to the geo-partitioned node in our user's local geography.&lt;/p&gt;

&lt;h2&gt;
  
  
  Counting Down
&lt;/h2&gt;

&lt;p&gt;So, these are just a few of the many distributed database configurations at your disposal. Which you should choose is totally dependent on your application's needs. However, more often than not, distributing your data layer will improve latency (as well as resiliency, data compliance, and more!)&lt;/p&gt;

&lt;p&gt;Look out for my next article on managing your database connections in Node.js!&lt;/p&gt;

</description>
      <category>yugabytedb</category>
      <category>postgres</category>
      <category>node</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Managing Your Distributed Node.js Application Environment and Configuration</title>
      <dc:creator>Brett Hoyer</dc:creator>
      <pubDate>Wed, 19 Oct 2022 20:23:49 +0000</pubDate>
      <link>https://forem.com/yugabyte/managing-your-distributed-nodejs-application-environment-and-configuration-236c</link>
      <guid>https://forem.com/yugabyte/managing-your-distributed-nodejs-application-environment-and-configuration-236c</guid>
      <description>&lt;p&gt;&lt;em&gt;See how to effectively manage the environment and configuration of your distributed Node.js applications.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Node.js applications rely on &lt;a href="https://en.wikipedia.org/wiki/Environment_variable" rel="noopener noreferrer"&gt;environment variables&lt;/a&gt; to differentiate between…environments!&lt;/p&gt;

&lt;p&gt;This means that applications running locally often behave differently to those being deployed to testing, staging, or production environments.  Often, this just means listening on a different port, or pointing to a different database url.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/bretthoyer/series/19070"&gt;The Largest River&lt;/a&gt;, my first foray into distributed application development, leverages both environment variables and configuration files to differentiate between the mode in which the code is being run, and the cloud region where it's being run.&lt;/p&gt;

&lt;p&gt;In this article, I'm going to demonstrate how the &lt;a href="https://www.npmjs.com/package/dotenv" rel="noopener noreferrer"&gt;dotenv&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/config" rel="noopener noreferrer"&gt;node-config&lt;/a&gt; NPM packages can be used together to keep your Node.js application code organized across environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting the Environment
&lt;/h2&gt;

&lt;p&gt;Application configuration can be done in a variety of ways, depending on the complexity of the system being built. In The Largest River project, I've chosen to use environment variables minimally, only setting &lt;code&gt;NODE_APP_INSTANCE&lt;/code&gt; and &lt;code&gt;NODE_ENV&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;Let's see how this works.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#.env
NODE_APP_INSTANCE=los-angeles
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# server.js

if (process.env.NODE_ENV === "development") { 
  require("dotenv").config();
}

// rest of file has access to process.env.NODE_APP_INSTANCE
if (process.env.NODE_APP_INSTANCE === “london”) {
  // connect to database node nearest to London
}
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might be wondering where I've set the value of &lt;code&gt;NODE_ENV&lt;/code&gt;. Well, this variable is set to 'development' by default. When running our node script, the following two commands are equivalent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NODE_ENV=development node server.js

node server.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In production, we set the variables explicitly in the startup script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# startup_script.sh
...
// setting NODE_APP_INSTANCE environment variable from instance metadata
NODE_APP_INSTANCE=$(curl http://metadata.google.internal/computeMetadata/v1/instance/attributes/instance_id -H "Metadata-Flavor: Google")
echo "NODE_APP_INSTANCE=${NODE_APP_INSTANCE}" | sudo tee -a /etc/environment

//setting NODE_ENV to production
echo "NODE_ENV=production" | sudo tee -a /etc/environment
source /etc/environment

//starting our node server with explicit command line arguments
NODE_ENV=$NODE_ENV NODE_APP_INSTANCE=$NODE_APP_INSTANCE node server.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures that even on server reboots, our application will start reliably, with the proper environment set. The &lt;code&gt;/etc/environments&lt;/code&gt; file is system-wide and persistent, making it a reasonable place to store our environment variables on production servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond the Basics
&lt;/h2&gt;

&lt;p&gt;With our environment settled, it's time to check out our application configuration.  &lt;/p&gt;

&lt;p&gt;What if our app needs more expressive configuration? Environment variables &lt;em&gt;can&lt;/em&gt; do the job, but their values are always strings. What if we want to include objects and nested data types?  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://12factor.net/config" rel="noopener noreferrer"&gt;Some developers&lt;/a&gt; would say, "&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify" rel="noopener noreferrer"&gt;Stringify your JSON&lt;/a&gt; and add it to your environment."&lt;/p&gt;

&lt;p&gt;While I understand the sentiment, I disagree. It is much more pleasant to work directly inside a JSON file. So long as you take care to not check sensitive information into source control, this can be a powerful tool in your development arsenal.&lt;/p&gt;

&lt;p&gt;Here's an example, utilizing the node-config package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# default.json
{
  "Databases": [
    {
      "id": "single_region",
      "type": "single_region",
      "host": "[HOST_FOR_DATABASE]", 
      "primaryRegion": "us-west2",
      "username": "[DB_USERNAME]",
      "password": "[DB_PASSWORD]",
      "cert": "[PATH_TO_DB_CERT]",
      "dev_cert": "[PATH_TO_CERT_IN_DEVELOPMENT]",
      "label": "Single-region, multi-zone",
      "sublabel": "3 nodes deployed in US West",
      "nodes": [
        {
          "coords": [35.37387, -119.01946],
          "label": "Bakersfield",
          "zone": "us-west2",
        },
        {
          "coords": [34.95313, -120.43586],
          "label": "Santa Maria",
          "zone": "us-west2",
        },
        {
          "coords": [32.71742, -117.16277],
          "label": "San Diego",
          "zone": "us-west2",
        }
      ]
    },
    ...
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# server.js

const config = require("config");
const Databases = config.get("Databases");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By utilizing a JSON configuration file, we're able to easily edit our database details. Given that The Largest River features multiple &lt;a href="https://www.yugabyte.com/managed/" rel="noopener noreferrer"&gt;YugabyteDB Managed&lt;/a&gt; databases with varying deployments, it is helpful to not be restricted to key-value string pairs.&lt;/p&gt;

&lt;p&gt;As a matter of interest, this is what working with the same configuration would look like in an environment variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .env
DATABASES='{"Databases":[{"id":"single_region","type":"single_region","host":"[HOST_FOR_DATABASE]","primaryRegion":"us-west2","username":"[DB_USERNAME]","password":"[DB_PASSWORD]","cert":"[PATH_TO_DB_CERT]","dev_cert":"[PATH_TO_CERT_IN_DEVELOPMENT]","label":"Single-region, multi-zone","sublabel":"3 nodes deployed in US West","nodes":[{"coords":[35.37387,-119.01946],"label":"Bakersfield","zone":"us-west2"},{"coords":[34.95313,-120.43586],"label":"Santa Maria","zone":"us-west2"},{"coords":[32.71742,-117.16277],"label":"San Diego","zone":"us-west2"}]}]}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# server.js
if (process.env.NODE_ENV === "development") { 
  require("dotenv").config();
}

const Databases = JSON.parse(process.env.DATABASES)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Earlier we set an environment variable called &lt;code&gt;NODE_APP_INSTANCE&lt;/code&gt;.  &lt;a href="https://github.com/node-config/node-config/wiki/Environment-Variables#node_app_instance" rel="noopener noreferrer"&gt;This variable&lt;/a&gt; can be used to set instance-specific configuration in a multi-instance deployment. This can be hugely helpful if instances of the same application need to behave differently.  &lt;/p&gt;

&lt;p&gt;The Largest River is comprised of six application instances, each connecting to different database nodes, depending on their configuration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiztkfngvtddkh6r88vex.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiztkfngvtddkh6r88vex.png" alt="The Largest River Application Instances" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;
The Largest River Application Instances



&lt;p&gt;If a &lt;code&gt;default.json&lt;/code&gt; needs to be overridden, these application instances will read from &lt;code&gt;production-los-angeles.json&lt;/code&gt;, &lt;code&gt;development-mumbai.json&lt;/code&gt;, etc. depending on the environment and cloud region.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;The way you manage your application environment and configuration is entirely up to you. After all, you're the one that has to work with it!  &lt;/p&gt;

&lt;p&gt;There are no hard and fast rules. Personally, I'm always looking for ways to remove configuration details from my application logic. This has a number of benefits. For example, as my applications evolve, there is a single source of truth, and reduced code duplication.  &lt;/p&gt;

&lt;p&gt;I hope you'll consider some of these tips when you create your next distributed Node application. And remember, this is your journey, do what works best for &lt;em&gt;you&lt;/em&gt;!&lt;/p&gt;

&lt;p&gt;Look out for my next blog, which will look at &lt;a href="https://www.yugabyte.com/tech/distributed-sql/" rel="noopener noreferrer"&gt;distributed SQL&lt;/a&gt; database configurations in depth!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>programming</category>
      <category>api</category>
    </item>
    <item>
      <title>Developing a Node.js Application in a Virtual Private Cloud</title>
      <dc:creator>Brett Hoyer</dc:creator>
      <pubDate>Mon, 10 Oct 2022 20:32:02 +0000</pubDate>
      <link>https://forem.com/yugabyte/developing-a-nodejs-application-in-a-virtual-private-network-29k7</link>
      <guid>https://forem.com/yugabyte/developing-a-nodejs-application-in-a-virtual-private-network-29k7</guid>
      <description>&lt;p&gt;Distributed applications rely on Virtual Private Clouds (&lt;a href="https://www.cloudflare.com/learning/cloud/what-is-a-virtual-private-cloud/" rel="noopener noreferrer"&gt;VPCs&lt;/a&gt;) to increase security and reduce latencies. Traffic is routed through the VPC, rather than the public internet, eliminating the need for any network hops along the way. &lt;/p&gt;

&lt;p&gt;In my continued development of &lt;a href="https://dev.to/bretthoyer/series/19070"&gt;The Largest River&lt;/a&gt;, I've chosen to deploy my application instances inside of a &lt;a href="https://cloud.google.com/vpc" rel="noopener noreferrer"&gt;Google Cloud VPC&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;To access these resources in the cloud, both in development and production environments, there are multiple considerations. How will I connect to my &lt;a href="https://www.yugabyte.com/yugabytedb" rel="noopener noreferrer"&gt;YugabyteDB&lt;/a&gt; clusters? And how will I connect from my local machine to remote resources in the VPC?  &lt;/p&gt;

&lt;p&gt;The following outlines my solutions to these problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  YugabyteDB Deployment in a VPC
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.yugabyte.com/managed/" rel="noopener noreferrer"&gt;YugabyteDB Managed&lt;/a&gt; relies on &lt;a href="https://docs.yugabyte.com/preview/yugabyte-cloud/cloud-basics/cloud-vpcs/" rel="noopener noreferrer"&gt;VPC Peering&lt;/a&gt;, to keep network traffic within the cloud provider's network and to establish connectivity between nodes from different cloud regions. &lt;/p&gt;

&lt;p&gt;This is an optional configuration in single-region clusters, but for multi-region deployments, it is mandatory. I’ve configured two multi-region clusters for this application, so VPC peering is required.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxlcs5f1wrdp6bzdetszy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxlcs5f1wrdp6bzdetszy.png" alt="YugabyteDB Managed" width="800" height="862"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For this to work, I also need to set up &lt;a href="https://cloud.google.com/vpc/docs/vpc-peering" rel="noopener noreferrer"&gt;VPC network peering&lt;/a&gt; in the Google Cloud.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmo3btlj6tnaxdee59twd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmo3btlj6tnaxdee59twd.png" alt="Google Cloud VPC Network Peering" width="800" height="668"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That was easy! &lt;/p&gt;

&lt;p&gt;Now our application nodes and database nodes have a peered network connection inside of Google Cloud. This also means that all of our database connections must come from machines inside of this network. &lt;/p&gt;

&lt;p&gt;What does this mean for local development?&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up SSH Tunneling
&lt;/h2&gt;

&lt;p&gt;This was uncharted territory for me. I needed to find a way to establish database connections to various multi-node clusters from my development machine, in order to iterate quickly.  &lt;/p&gt;

&lt;p&gt;Initially, I thought about writing application code directly on a VM using &lt;a href="(https://code.visualstudio.com/docs/remote/ssh)"&gt;remote SSH&lt;/a&gt; in Visual Studio Code. This was problematic, in that I would still need to push and update the code on all other application instances. I needed a solution which didn’t require continuously deploying code to remote servers within the VPC for testing.   &lt;/p&gt;

&lt;p&gt;Here is where I discovered the power of &lt;a href="https://www.ssh.com/academy/ssh/tunneling" rel="noopener noreferrer"&gt;SSH tunneling&lt;/a&gt; (also known as port forwarding). By tunneling connections from my local machine through a server in the VPC, I'm able to establish a database connection to each and every database node relying on this peered VPC connection.&lt;/p&gt;

&lt;p&gt;Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# development machine command line
ssh -N -i /path/to/gcp/ssh/key -L 5000:fake-database-node-url.gcp.ybdb.io:5433 vm_username@[VM_IP_ADDRESS]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command forwards &lt;code&gt;localhost:5000&lt;/code&gt; to &lt;code&gt;fake-database-node-url.gcp.ybdb.io:5433&lt;/code&gt;, through a VM inside of the VPC, to which I'm able to establish and SSH connection. &lt;/p&gt;

&lt;p&gt;Despite being incredibly simple to set up, this discovery has been instrumental in my development journey. By utilizing port forwarding, my Node.js application development environment has remained unchanged. &lt;/p&gt;

&lt;p&gt;When the &lt;code&gt;NODE_ENV&lt;/code&gt; environment variable is set to &lt;code&gt;development&lt;/code&gt;, the app knows to establish connections through a particular local port for each database node. When the environment variable is set to &lt;code&gt;production&lt;/code&gt;, the application knows requests should be made directly to the database URL. It just works, and it does so securely. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgcpqwbveqyobxm4yjvvs.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgcpqwbveqyobxm4yjvvs.gif" alt="It just works" width="498" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Much the same, SSH tunneling can be used within a database client.  Here's what this looks like inside of my client of choice, (DBeaver)[&lt;a href="https://dbeaver.io/" rel="noopener noreferrer"&gt;https://dbeaver.io/&lt;/a&gt;].&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuovwtk7jc46i446bgmuc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuovwtk7jc46i446bgmuc.png" alt="DBeaver SSH Tunneling" width="695" height="613"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;For those working as IT administrators, devops, or network engineers, etc., this might seem like a rudimentary discovery!  &lt;/p&gt;

&lt;p&gt;SSH tunneling has a wide range of use cases and I'm sure I've just scratched the surface. However, nothing feels better than finding a simple, frictionless solution to a blocking issue you've never faced before. &lt;/p&gt;

&lt;p&gt;Without SSH tunneling, I was essentially stuck, doomed only to deploy my code to an instance within the VPC in order to test its validity. I will not be apologizing for my excitement at this time. 😎&lt;/p&gt;

&lt;p&gt;We’re approaching the finish line, stay tuned!&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>yugabytedb</category>
    </item>
    <item>
      <title>Deploying a Node.js application across multiple geographies with Terraform and Ansible</title>
      <dc:creator>Brett Hoyer</dc:creator>
      <pubDate>Fri, 16 Sep 2022 17:21:49 +0000</pubDate>
      <link>https://forem.com/yugabyte/deploying-a-nodejs-application-across-multiple-geographies-with-terraform-and-ansible-20n7</link>
      <guid>https://forem.com/yugabyte/deploying-a-nodejs-application-across-multiple-geographies-with-terraform-and-ansible-20n7</guid>
      <description>&lt;p&gt;A &lt;a href="https://dev.to/yugabyte/the-largest-river-part-1-first-steps-to-building-a-globally-distributed-application-47ek"&gt;plan&lt;/a&gt; is in place, &lt;a href="https://dev.to/yugabyte/geo-distributed-applications-using-terraforms-infrastructure-automation-3mj3"&gt;infrastructure&lt;/a&gt; is provisioned, now what? &lt;/p&gt;

&lt;p&gt;Well, I've decided to take a step back and review the distributed system holistically, before surging forward with application development.&lt;/p&gt;

&lt;p&gt;Which system level improvements can I make to increase the speed with which I can iterate on my designs, test my code, and alert myself to potential bottlenecks?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F03mkgs38a0chuaf6r6kr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F03mkgs38a0chuaf6r6kr.png" alt="Phone Location" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbif88qz2vkrdudl4i546.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbif88qz2vkrdudl4i546.png" alt="Main View" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s a sneak peek into the globally-distributed bookstore that I’ve been working on. Users are able to simulate connecting from 6 different locations across the globe, Los Angeles, Washington, D.C., São Paulo, London, Mumbai and Sydney.  Multiple &lt;a href="https://www.yugabyte.com/managed/" rel="noopener noreferrer"&gt;YugabyteDB Managed&lt;/a&gt; database clusters are deployed to support this application.  So far, I've deployed 3 clusters, a single-region cluster, a multi-region cluster w/ &lt;a href="https://docs.yugabyte.com/preview/explore/multi-region-deployments/read-replicas-ysql/" rel="noopener noreferrer"&gt;read replicas&lt;/a&gt; and a &lt;a href="https://docs.yugabyte.com/preview/explore/multi-region-deployments/row-level-geo-partitioning/" rel="noopener noreferrer"&gt;geo-partitioned&lt;/a&gt; cluster. Users may choose which database the application connects to, in order to highlight the latency discrepancies between database configurations. &lt;/p&gt;

&lt;p&gt;In this blog, I’ll explain how multiple Node.js servers are deployed across these geographies to make this possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Changing it up
&lt;/h2&gt;

&lt;p&gt;The main benefit of &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; is the ability to easily change cloud infrastructure when plans change. &lt;/p&gt;

&lt;p&gt;Well, plans have changed! &lt;/p&gt;

&lt;p&gt;I have decided to scrap Google's &lt;a href="https://cloud.google.com/container-optimized-os/docs" rel="noopener noreferrer"&gt;Container-Optimized OS&lt;/a&gt; in favor of &lt;a href="https://cloud.google.com/compute/docs/create-linux-vm-instance" rel="noopener noreferrer"&gt;Ubuntu&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;In the short term, this will increase my productivity. I'll be able to update my code and manipulate the environment without needing to push builds to &lt;a href="https://cloud.google.com/container-registry" rel="noopener noreferrer"&gt;Google Container Registry&lt;/a&gt;. Change can be hard, but with Terraform, it's incredibly easy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# main.tf

boot_disk {
   initialize_params {
       image = "cos-cloud/cos-stable" //OLD

       image = "ubuntu-os-cloud/ubuntu-2004-lts" //NEW
   }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the only configuration change required to provision new infrastructure, with the OS of my choosing. The beauty of automation is that the rest of the system, the networking, the instance sizes, and the locations, remain unchanged. A little upfront configuration saved me a ton of time and a lot of headaches.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb4tjnh3dpyxwf49pbg1l.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb4tjnh3dpyxwf49pbg1l.gif" alt="Easy work" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Automating Application Deployment
&lt;/h2&gt;

&lt;p&gt;However, it's not all rainbows and butterflies! &lt;/p&gt;

&lt;p&gt;You might recall that in my &lt;a href="https://dev.to/yugabyte/geo-distributed-applications-using-terraforms-infrastructure-automation-3mj3"&gt;previous post&lt;/a&gt;, I outlined how Google's Container-Optimized OS automatically pulls and runs container images upon provisioning infrastructure. &lt;/p&gt;

&lt;p&gt;Now that we're no longer running containers on our VMs, we'll need to deploy and run our code another other way. &lt;/p&gt;

&lt;p&gt;Fear not, there are many tools out there which makes this a breeze. I've chosen to use to use &lt;a href="https://www.ansible.com/" rel="noopener noreferrer"&gt;Ansible&lt;/a&gt; for my code automation. &lt;/p&gt;

&lt;p&gt;Let's dive into it, starting with some configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# inventory.yml

[Private]
10.168.0.2 #Los Angeles
10.154.0.2 #London
10.160.0.2 #Mumbai
10.158.0.2 #Sao Paulo
10.152.0.2 #Sydney
10.150.0.2 #Washington, D.C.

[Private:vars]
ansible_ssh_common_args= '-o ProxyCommand="ssh -W %h:%p -q [USERNAME]@[IP_ADDRESS]"'
ansible_ssh_extra_args= '-o StrictHostKeyChecking=no'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, I'm setting the IP addresses of the 6 application instances we provisioned using Terraform, plus some variables to be used by our Ansible playbook.&lt;/p&gt;

&lt;p&gt;You might have noticed that I've set internal IP addresses, which typically cannot be accessed from outside of the private network. This is correct, and I'll be covering how this works in my next blog in this series (hint: SSH tunneling). For now, just assume these are publicly-accessible addresses.&lt;/p&gt;

&lt;p&gt;Now, on to the playbook…&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# playbook.yml

---
- hosts: all
 become: yes
 vars:
   server_name: "{{ ansible_default_ipv4.address }}"
   document_root: /home/
   app_root: ../api_service/
 tasks:
   - name: Copy api service to /home directory
     synchronize:
       src: "{{ app_root }}"
       dest: "{{ document_root }}"
       rsync_opts:
         - "--no-motd"
         - "--exclude=node_modules"
         - "--rsh='ssh {{ ansible_ssh_common_args }} {{ ansible_ssh_extra_args }}'"

   - name: Install project dependencies
     command: sudo npm install --ignore-scripts
     args:
       chdir: /home/

   - name: Kill Node Server
     command: sudo pm2 kill
     args:
       chdir: /home/

   - name: Restart Node Server
     shell: NODE_ENV=$NODE_ENV NODE_APP_INSTANCE=$NODE_APP_INSTANCE sudo pm2 start index.js --name node-app
     args:
       chdir: /home/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script lays out the steps required to provision our code and run our Node.js server. &lt;/p&gt;

&lt;p&gt;We start by reading the hosts from our &lt;code&gt;inventory.yml&lt;/code&gt; file and using rsync to push code to each of them in sequence. &lt;/p&gt;

&lt;p&gt;Our application dependencies are installed, the server is stopped if currently running, and then restarted with environment variables set on each machine. This is all remarkably easy to set up, understand, and replicate. &lt;/p&gt;

&lt;p&gt;We now essentially have just two buttons to push - one to spin up our VMs and another to provision our application code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting Fresh
&lt;/h2&gt;

&lt;p&gt;You might be wondering where those environment variables are being set on our Ubuntu VMs. Don't we need to configure these instances and install system level dependencies? &lt;/p&gt;

&lt;p&gt;We sure do! &lt;/p&gt;

&lt;p&gt;We can add a startup script to our Terraform config which will run whenever an instance starts or reboots.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# startup_script.sh
#! /bin/bash

if [ ! -f "/etc/initialized_on_startup" ]; then
   echo "Launching the VM for the first time."

   sudo apt update
   sudo apt-get update
   curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash -
   sudo apt-get install -y nodejs
   sudo npm install pm2 -g
   NODE_APP_INSTANCE=$(curl http://metadata.google.internal/computeMetadata/v1/instance/attributes/instance_id -H "Metadata-Flavor: Google")
   echo "NODE_APP_INSTANCE=${NODE_APP_INSTANCE}" | sudo tee -a /etc/environment
   echo $NODE_APP_INSTANCE
   echo "NODE_ENV=production" | sudo tee -a /etc/environment
   source /etc/environment
   sudo touch /etc/initialized_on_startup
else
   # Executed on restarts
   source /etc/environment
   cd /home/api_service &amp;amp;&amp;amp; NODE_ENV=$NODE_ENV NODE_APP_INSTANCE=$NODE_APP_INSTANCE sudo pm2 start index.js --name node-app
fi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The startup script installs Node.js and PM2 and reads from our instance metadata to set the NODE_APP_INSTANCE environment variable. &lt;/p&gt;

&lt;p&gt;This environment variable will be used in our application to determine where our application is running, and which database nodes the API service should connect to. I’ll  cover this in more detail in a future post. &lt;/p&gt;

&lt;p&gt;If our VM requires a restart, this script will re-run our Node server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# main.tf
variable "instances" {
 type = map(object({
   name = string
   zone = string
   network_ip = string
   metadata = object({ instance_id = string})
   startup_script = string
 }))
 default = {
   "los_angeles" = {
     metadata = {instance_id = "los-angeles"}
     startup_script = "startup_script.sh",
     name = "los-angeles"
     zone = "us-west2-a" // Los Angeles, CA
     network_ip = "10.168.0.2"
   },
   ...
 }
}

resource "google_compute_instance" "vm_instance" {
   ...
   metadata_startup_script = file("${path.module}/${each.value.startup_script}")
   ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After writing this basic startup script, I've added it to the Terraform config, so that it's run on all provisioned API service instances. &lt;/p&gt;

&lt;p&gt;I now have a great foundation with which to build out my application logic. My infrastructure and application code have been deployed in a way that is easily replicable.&lt;/p&gt;

&lt;p&gt;I'm looking forward to wrapping up the application logic, so I can unveil the final product. I guess I better get coding…&lt;br&gt;&lt;br&gt;
Follow along for more updates!&lt;/p&gt;

</description>
      <category>ansible</category>
      <category>terraform</category>
      <category>webdev</category>
      <category>yugabytedb</category>
    </item>
    <item>
      <title>Automating Infrastructure For Geo-Distributed Applications With Terraform</title>
      <dc:creator>Brett Hoyer</dc:creator>
      <pubDate>Thu, 28 Jul 2022 20:59:00 +0000</pubDate>
      <link>https://forem.com/yugabyte/geo-distributed-applications-using-terraforms-infrastructure-automation-3mj3</link>
      <guid>https://forem.com/yugabyte/geo-distributed-applications-using-terraforms-infrastructure-automation-3mj3</guid>
      <description>&lt;p&gt;An increasing number of products are being developed with performance and scalability in mind. Modern developers need to rely on improved tooling to efficiently and reliably build, test and deploy their applications.  &lt;/p&gt;

&lt;p&gt;Most of us have moved on from hosting applications on machines in our bedroom closets. Instead we now choose to deploy to instances managed by &lt;a href="https://aws.amazon.com/" rel="noopener noreferrer"&gt;Amazon Web Services&lt;/a&gt;, &lt;a href="https://cloud.google.com/" rel="noopener noreferrer"&gt;Google Cloud&lt;/a&gt;, or one of countless other cloud providers.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fek83kj5uf2jrh7gfppnz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fek83kj5uf2jrh7gfppnz.jpg" alt="Server Room Disaster" width="500" height="375"&gt;&lt;/a&gt;&lt;/p&gt;
"This looks like fun!" - Nobody



&lt;p&gt; &lt;/p&gt;

&lt;p&gt;To continue my development of &lt;a href="https://dev.to/yugabyte/the-largest-river-part-1-first-steps-to-building-a-globally-distributed-application-47ek"&gt;The Largest River&lt;/a&gt; (my first foray into global application development), I’ve chosen to use &lt;a href="https://cloud.google.com/compute" rel="noopener noreferrer"&gt;Google Compute Engine&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;Major cloud providers like Google provide both web and command-line interfaces to create and manage virtual machines, as well as offering countless other products. However, when deploying apps at scale, this can become quite an arduous task! What if the deployment environment needs to be replicated, say, to create a test or staging environment? Surely, we’d want this environment to directly mirror that of production.  Any configuration step which isn’t automated leaves us open to unexpected bugs.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frdc1wrm8azlo2p75emk0.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frdc1wrm8azlo2p75emk0.gif" alt="Unexpected Bugs" width="480" height="362"&gt;&lt;/a&gt;&lt;br&gt;
 &lt;/p&gt;
&lt;h2&gt;
  
  
  Infrastructure as Code
&lt;/h2&gt;

&lt;p&gt;Rather than relying on time-consuming manual tasks or a series of shell scripts to create and modify infrastructure, I’ve decided to use &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; to do this from a single configuration file. &lt;/p&gt;

&lt;p&gt;Although I’ve only scratched the surface with its functionality, the benefits are immediately apparent.  Here are some of the ways I’ve begun using Terraform with The Largest River.&lt;/p&gt;

&lt;p&gt;Using the &lt;a href="https://registry.terraform.io/providers/hashicorp/google/latest/docs" rel="noopener noreferrer"&gt;Google Cloud Platform Provider&lt;/a&gt;, I’m easily able to spin up and tear down my project infrastructure on GCP.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# main.tf

terraform {
 required_providers {
   google = {
     source  = "hashicorp/google"
     version = "4.24.0"
   }
  }
}

provider "google" {
 credentials = file([path_to_json_keyfile_downloaded_from_gcp])
 project = [gcp_project_name]
 region  = "us-central1"
 zone    = "us-central1-c"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After downloading my JSON keyfile from GCP, I’ve set up the Google provider, which can be used to securely connect to my project in the cloud.  Although my application resources will be spread across multiple &lt;a href="https://cloud.google.com/compute/docs/regions-zones" rel="noopener noreferrer"&gt;regions and zones&lt;/a&gt;, I’ve chosen the us-central1 region and us-central1-c zone as my project defaults.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping Things Private
&lt;/h2&gt;

&lt;p&gt;With the Google provider configured, I’m able to set up network, firewall, and instance resources. &lt;/p&gt;

&lt;p&gt;In the case of The Largest River, setting up a &lt;a href="https://cloud.google.com/vpc/docs/vpc" rel="noopener noreferrer"&gt;VPC network&lt;/a&gt; is important for multiple reasons:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I’ll be able to communicate between servers in a multi-region deployment, without having to traverse the public internet. This comes with latency and security benefits.
&lt;/li&gt;
&lt;li&gt;As I’ll use this VPC for my multi-region &lt;a href="https://www.yugabyte.com/managed/" rel="noopener noreferrer"&gt;YugabyteDB Managed&lt;/a&gt; deployments, my application servers and database nodes will live within the same global network.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s how the network is initialized using the &lt;a href="https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/compute_network" rel="noopener noreferrer"&gt;google_compute_network&lt;/a&gt; resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "google_compute_network" "vpc_network" {
 name = "tlr-network"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make use of this network, I’ve added some &lt;a href="https://cloud.google.com/vpc/docs/firewalls" rel="noopener noreferrer"&gt;firewall&lt;/a&gt; rules to enable SSH and HTTP traffic to my instances, using the &lt;a href="https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_firewall" rel="noopener noreferrer"&gt;google_compute_firewall&lt;/a&gt; resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "google_compute_firewall" "ssh-rule" {
 name    = "ssh-rule"
 network = google_compute_network.vpc_network.name
 allow {
   protocol = "tcp"
   ports    = ["22"]
 }
 target_tags   = ["allow-ssh"]
 source_ranges = ["0.0.0.0/0"]
}

resource "google_compute_firewall" "http-rule" {
 name    = "http-rule"
 network = google_compute_network.vpc_network.name
 allow {
   protocol = "tcp"
   ports    = ["80", "8080"]
 }
 target_tags   = ["allow-http"]
 source_ranges = ["0.0.0.0/0"]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These rules include some new parameters, namely, target_tags and source_ranges: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Target tags apply specific firewall rules to an instance in the network. We’ll see how this works soon, as I begin to configure some virtual machines.
&lt;/li&gt;
&lt;li&gt;Source ranges determine the IP addresses this rule applies to.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this example, we’ve opened up connections to the whole internet, which, you know, isn’t great! In reality, we’d set our source ranges to a sensible range within our subnet for instances that don’t need to be exposed to the public internet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the Right Compute Instances
&lt;/h2&gt;

&lt;p&gt;What good is a network without any instances?  We currently have roads leading to nowhere.  Let’s change that, using the &lt;a href="https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance" rel="noopener noreferrer"&gt;google_compute_instance &lt;/a&gt; resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;variable "instances" {
 type = map(object({
   name = string
   zone = string
 }))
 default = {
   "usa" = {
     name = "instance-usa"
     zone = "us-central1-c"
   },
   "europe" = {
     name = "instance-europe"
     zone = "europe-west3-b"
   },
   "asia" = {
     name = "instance-asia"
     zone = "asia-east1-a"
   }
 }
}

resource "google_compute_instance" "vm_instance" {
 for_each = var.instances

 name                      = each.value.name
 machine_type              = "f1-micro"
 zone                      = each.value.zone
 allow_stopping_for_update = true

 tags = ["allow-ssh", "allow-http"]

 boot_disk {
   initialize_params {
     image = "cos-cloud/cos-stable"
   }
 }

 metadata = {
   gce-container-declaration = "spec:\n  containers:\n    - name: tlr-api\n      image: [container_image_url_in_gcr]\n      stdin: false\n      tty: false\n  restartPolicy: Always\n"
 }

 service_account {
   email = [email_for_project_service_account]
   scopes = [
     "https://www.googleapis.com/auth/cloud-platform"
   ]
 }

 network_interface {
   network = google_compute_network.vpc_network.name
   access_config {
   }
 }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have some containerized houses at the end of the road, we’re starting to get somewhere...  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4vbu07gs53h60j7p4k5d.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4vbu07gs53h60j7p4k5d.jpeg" alt="Container House" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;
(Cringeworthy developer joke, my apologies.)



&lt;p&gt; &lt;/p&gt;

&lt;p&gt;In all seriousness, GCP offers a wide range of instance types.  For The Largest River, I’ve chosen &lt;a href="https://cloud.google.com/container-registry" rel="noopener noreferrer"&gt;Google Container Registry&lt;/a&gt;, and thus, a &lt;a href="https://cloud.google.com/container-optimized-os/docs/concepts/features-and-benefits#:~:text=Container%2DOptimized%20OS%20from%20Google,open%20source%20Chromium%20OS%20project." rel="noopener noreferrer"&gt;Container-Optimized OS&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;To deploy to multiple zones without code duplication, I’ve set the &lt;strong&gt;instances&lt;/strong&gt; &lt;a href="https://www.terraform.io/language/values/variables" rel="noopener noreferrer"&gt;variable&lt;/a&gt;, which is looped to create instances in the USA, Europe and Asia. I’ve added two tags to these instances, &lt;em&gt;allow-ssh&lt;/em&gt; and &lt;em&gt;allow-http&lt;/em&gt;. These tags match the target tags specified in our firewall rule blocks, which means these rules will be applied to the deployed instances.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;With the core elements defined in our configuration, we can make use of the &lt;a href="https://www.terraform.io/cli/commands" rel="noopener noreferrer"&gt;Terraform CLI&lt;/a&gt; to provision the infrastructure.  You don’t even need to click in the GCP console as Terraform elegantly tracks changes to this configuration, making planning and updating a breeze. &lt;/p&gt;

&lt;p&gt;Much like core app development, the infrastructure as code community has fully adopted code reuse and expressive language support. The &lt;a href="https://www.terraform.io/language" rel="noopener noreferrer"&gt;Terraform Language&lt;/a&gt; includes many such features and I look forward to diving deeper, as I continue to build this geo-distributed application. You can revisit the start of my journey &lt;a href="https://dev.to/yugabyte/the-largest-river-part-1-first-steps-to-building-a-globally-distributed-application-47ek"&gt;here&lt;/a&gt;, and stay tuned for more updates!&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>yugabytedb</category>
      <category>distributedsql</category>
      <category>googlecloud</category>
    </item>
    <item>
      <title>First Steps to Building a Globally-Distributed Application</title>
      <dc:creator>Brett Hoyer</dc:creator>
      <pubDate>Wed, 29 Jun 2022 18:12:00 +0000</pubDate>
      <link>https://forem.com/yugabyte/the-largest-river-part-1-first-steps-to-building-a-globally-distributed-application-47ek</link>
      <guid>https://forem.com/yugabyte/the-largest-river-part-1-first-steps-to-building-a-globally-distributed-application-47ek</guid>
      <description>&lt;p&gt;As software developers, we’re often prompted to learn new technologies, either by our employers, or by our own curiosities.  This endless learning is one of the primary reasons we got into this field to begin with.  UI developers wish they had a deeper understanding of backend frameworks, and backend developers wish they could write CSS transitions and animations (no they don’t, but you get what I mean).  &lt;/p&gt;

&lt;p&gt;Throughout my own software journey, my desire to enhance my skills across the stack has sent me down a seemingly endless maze of blog posts, tutorials and instructional videos.  While these mediums serve their purpose, I’m often left wanting to learn through my own explorations and failures to determine what’s “best”.  &lt;/p&gt;

&lt;p&gt;As such, I’ve started to build a new globally-distributed application called “The Largest River” that will certainly satisfy this desire. This blog series will highlight my discoveries, shortcomings, and everything in between as I work to complete this project.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Project
&lt;/h2&gt;

&lt;p&gt;Today’s application development landscape is drastically different than that of years past. We’re handling scalability in new and exciting ways, and serving traffic from all over the globe. This is what I want to focus on. How can I build a distributed application that will service a global marketplace? We’ve all built more than our fair share of “to-do list” applications.  This will not be one of them.&lt;/p&gt;

&lt;p&gt;There are few key aspects I’d like to highlight:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Serving traffic globally with low latency&lt;/li&gt;
&lt;li&gt;Being resilient to potential zone or region outages&lt;/li&gt;
&lt;li&gt;Properly adhering to data compliance laws (for instance, all EU user data must be stored in the EU)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While the precise features of the application are immaterial, the architecture is of primary importance. A lot of tools (and buzzwords) come to mind when trying to architect a modern web application. Assets can be served from a &lt;a href="https://www.cloudflare.com/learning/cdn/what-is-a-cdn/" rel="noopener noreferrer"&gt;CDN&lt;/a&gt; to improve page load speed. A &lt;a href="https://cloud.google.com/load-balancing" rel="noopener noreferrer"&gt;global load balancer&lt;/a&gt; can front all traffic, sending requests to the nearest server. &lt;a href="https://cloud.google.com/serverless" rel="noopener noreferrer"&gt;Serverless functions&lt;/a&gt; and &lt;a href="https://vercel.com/docs/concepts/functions/edge-functions" rel="noopener noreferrer"&gt;edge functions&lt;/a&gt; can be used to handle requests, eliminating the need to manage infrastructure altogether. &lt;a href="https://kubernetes.io/" rel="noopener noreferrer"&gt;Kubernetes&lt;/a&gt; can be deployed for container orchestration, networking and healing, amongst many other production-grade features.  The list goes on.&lt;/p&gt;

&lt;p&gt;In an attempt to walk before I run, I’ve decided to start with a &lt;em&gt;relatively&lt;/em&gt; simple architecture.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F01mmo7pq55dcshp0vffc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F01mmo7pq55dcshp0vffc.png" alt="Architecture Diagram" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://reactjs.org/" rel="noopener noreferrer"&gt;React&lt;/a&gt; frontend sends traffic through an &lt;a href="https://www.nginx.com/" rel="noopener noreferrer"&gt;Nginx&lt;/a&gt; reverse proxy, to VMs running in multiple regions. Running VMs in multiple regions (once properly load-balanced) will result in shorter round trips, as well as enabling us to reroute traffic in the event of a region outage. These VMs are all running the same containerized &lt;a href="//https[:](url)//nodejs.org/en/"&gt;Node.js&lt;/a&gt; process, which creates a connection to a &lt;a href="https://www.yugabyte.com/" rel="noopener noreferrer"&gt;YugabyteDB&lt;/a&gt; database. YugabyteDB is a &lt;a href="https://www.yugabyte.com/tech/postgres-compatibility/" rel="noopener noreferrer"&gt;Postgres-compliant&lt;/a&gt;, highly-available, &lt;a href="https://www.yugabyte.com/tech/distributed-sql/" rel="noopener noreferrer"&gt;distributed SQL&lt;/a&gt; database. If you’d like to spin up an &lt;a href="https://cloud.yugabyte.com/signup" rel="noopener noreferrer"&gt;always-free single-node cluster&lt;/a&gt; for yourself, it is easy to do so.&lt;/p&gt;

&lt;p&gt;This architecture is intentionally a bit naive. I’m able to demonstrate that serving traffic to a single database node in another region comes with extremely high latencies. Businesses have operated this way for many years, scaling their databases vertically, at the cost of network latency (amongst many other things). As I continue to iterate on this design, I’ll deploy a multi-zone, multi-region database, which will be more representative of a modernized deployment. This will allow for both zone and region failures and enable data compliance, in addition to improving read and write latencies.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia2.giphy.com%2Fmedia%2Fl0IylOPCNkiqOgMyA%2Fgiphy.gif%3Fcid%3D790b7611e008b5d9002a012f5e5ab8259905b0a954c8c4ba%26rid%3Dgiphy.gif%26ct%3Dg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia2.giphy.com%2Fmedia%2Fl0IylOPCNkiqOgMyA%2Fgiphy.gif%3Fcid%3D790b7611e008b5d9002a012f5e5ab8259905b0a954c8c4ba%26rid%3Dgiphy.gif%26ct%3Dg" title="99% of the development process." alt="chaos gif" width="480" height="304"&gt;&lt;/a&gt;&lt;/p&gt;
99% of the development process.



&lt;h2&gt;
  
  
  The Development Environment
&lt;/h2&gt;

&lt;p&gt;I decided to use &lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; and Docker Compose to simulate this distributed environment on my local machine. Containerization affords me the ability to easily manage and isolate dependencies, while also mirroring the production environment. Through a single command, I’m able to spin up all of the processes locally, passing the environment variables required to make connections to my remote database. Additionally, I’m using &lt;a href="https://docs.docker.com/storage/volumes/" rel="noopener noreferrer"&gt;volumes&lt;/a&gt; to persist data, which affords me all of the niceties expected of modern application development, such as client reloads and server restarts on file changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Production Environment
&lt;/h2&gt;

&lt;p&gt;After countless hours of research and development, I’ve decided to run a &lt;a href="https://cloud.google.com/container-optimized-os/docs" rel="noopener noreferrer"&gt;Container-Optimized OS&lt;/a&gt; on &lt;a href="https://cloud.google.com/compute" rel="noopener noreferrer"&gt;Google Compute Engine VMs&lt;/a&gt;. These machines run images, which I’ve pushed to the &lt;a href="https://cloud.google.com/container-registry" rel="noopener noreferrer"&gt;Google Container Registry&lt;/a&gt;. As mentioned previously, this is helpful in that the same Docker images can be run locally and in production, with minimal differences in configuration.  &lt;/p&gt;

&lt;p&gt;Of course, this all sounds great, but how are the containers managed? How are they networked? In my career, I’ve rarely been faced with handling network traffic between multiple VMs and database nodes, so this learning curve is particularly steep. Thankfully, I’ve already made great progress (or so I think). I look forward to sharing my findings in future blog posts.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/nUoXu8yH0ZffVCudD0/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/nUoXu8yH0ZffVCudD0/giphy.gif" title="The 1% that makes it all worthwhile." alt="success gif" width="500" height="500"&gt;&lt;/a&gt;&lt;/p&gt;
The 1% that makes it all worthwhile.



</description>
      <category>node</category>
      <category>react</category>
      <category>yugabytedb</category>
      <category>distributedsql</category>
    </item>
  </channel>
</rss>
