<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Joan</title>
    <description>The latest articles on DEV Community by Joan (@joaning).</description>
    <link>https://dev.to/joaning</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%2F1109441%2F0fde7cec-f878-4fce-8e3d-f04e587b90a4.png</url>
      <title>DEV Community: Joan</title>
      <link>https://dev.to/joaning</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/joaning"/>
    <language>en</language>
    <item>
      <title>How pgroll works under the hood</title>
      <dc:creator>Joan</dc:creator>
      <pubDate>Thu, 07 Dec 2023 15:00:00 +0000</pubDate>
      <link>https://dev.to/xata/how-pgroll-works-under-the-hood-5ac2</link>
      <guid>https://dev.to/xata/how-pgroll-works-under-the-hood-5ac2</guid>
      <description>&lt;p&gt;At the start of October we released &lt;a href="https://github.com/xataio/pgroll" rel="noopener noreferrer"&gt;pgroll&lt;/a&gt;, an open source tool for &lt;a href="https://xata.io/blog/pgroll-schema-migrations-postgres" rel="noopener noreferrer"&gt;zero-downtime, reversible schema migrations for Postgres&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We've been hard at work on &lt;code&gt;pgroll&lt;/code&gt; for the past couple of months, and now seems like the perfect time to delve deeper into &lt;code&gt;pgroll&lt;/code&gt; and explore how it really works.&lt;/p&gt;

&lt;h2&gt;
  
  
  A brief recap
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;pgroll&lt;/code&gt; is an open source schema migration tool that takes a different approach, using the &lt;a href="https://openpracticelibrary.com/practice/expand-and-contract-pattern/" rel="noopener noreferrer"&gt;expand/contract pattern&lt;/a&gt;, to solve some of the problems associated with schema migrations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multiple schema versions&lt;/strong&gt;: &lt;code&gt;pgroll&lt;/code&gt; keeps two versions of your schema active at the same time during a migration; the old schema and the new. This means that old versions of client applications can co-exist with new versions of client applications during an application rollout because each version of the application sees the version of the database schema that it expects to see.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lock free migrations&lt;/strong&gt;: A careful approach to locking to ensure that tables don't get locked for long periods during a migration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy rollbacks&lt;/strong&gt;: Rollbacks are simplified with &lt;code&gt;pgroll&lt;/code&gt;, as it keeps two versions of your database schema during a migration. To rollback, you just need to remove the new schema version. Since the old version remains intact throughout, older client applications continue to work without any disruption&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data backfilling&lt;/strong&gt;: During a migration, &lt;code&gt;pgroll&lt;/code&gt; allows both old and new schema versions to coexist. It synchronizes any data changes between these versions. This means data added to the old schema is automatically updated in the new schema, and the reverse is also true. As a result, both old and new client applications can operate simultaneously without issues until the migration is complete.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A step-by-step example
&lt;/h2&gt;

&lt;p&gt;Let's see how &lt;code&gt;pgroll&lt;/code&gt; works by walking through an example migration and seeing what &lt;code&gt;pgroll&lt;/code&gt; does at each step of the migration process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initialization
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;pgroll&lt;/code&gt; needs to store its own internal state somewhere in the target Postgres database. Initializing &lt;code&gt;pgroll&lt;/code&gt; configures this store and makes &lt;code&gt;pgroll&lt;/code&gt; ready for first use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pgroll init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A message is displayed confirming the successful configuration of pgroll.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What data does &lt;code&gt;pgroll&lt;/code&gt; store?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pgroll&lt;/code&gt; stores its data in the &lt;code&gt;pgroll&lt;/code&gt; schema. In this schema it creates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;migrations&lt;/code&gt; table containing the version history for each schema in the database.&lt;/li&gt;
&lt;li&gt;Functions to capture the current database schema for a given schema name.&lt;/li&gt;
&lt;li&gt;Triggers to capture DDL statements that run outside of &lt;code&gt;pgroll&lt;/code&gt; migrations.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  First migration
&lt;/h3&gt;

&lt;p&gt;With &lt;code&gt;pgroll&lt;/code&gt; initialized, let's run our first migration. Here is a migration that creates a table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"01_create_users_table"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"operations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"create_table"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"columns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"serial"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"varchar(255)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"unique"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"nullable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, save this file as &lt;code&gt;sql/01_create_users_table.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The migration will create a &lt;code&gt;users&lt;/code&gt; table with three columns. It is equivalent to the following SQL DDL statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To apply the migration to the database, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pgroll start sql/01_create_users_table.json --complete
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What does the &lt;code&gt;--complete&lt;/code&gt; flag do here?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pgroll&lt;/code&gt; divides migration application into two steps: &lt;strong&gt;start&lt;/strong&gt; and &lt;strong&gt;complete&lt;/strong&gt;. During the &lt;strong&gt;start&lt;/strong&gt; phase, both old and new versions of the database schema are available to client applications. After the &lt;strong&gt;complete&lt;/strong&gt; phase, only the &lt;em&gt;most recent&lt;/em&gt; schema is available.&lt;/p&gt;

&lt;p&gt;As this is the first migration, there is no &lt;em&gt;old&lt;/em&gt; schema to maintain, so the migration can safely be started and completed in one step.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;The &lt;code&gt;users&lt;/code&gt; table can be filled with sample data using this SQL command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;SELECT&lt;/span&gt;
   &lt;span class="s1"&gt;'user_'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="k"&gt;CASE&lt;/span&gt;
     &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'description for user_'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;suffix&lt;/span&gt;
     &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
   &lt;span class="k"&gt;END&lt;/span&gt;
 &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;generate_series&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will insert 100,000 users into the &lt;code&gt;users&lt;/code&gt; table. Roughly half of the users will have descriptions and the other half will have &lt;code&gt;NULL&lt;/code&gt; descriptions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Second migration
&lt;/h3&gt;

&lt;p&gt;Now that the &lt;code&gt;users&lt;/code&gt; table is set up, let's apply a non-backwards-compatible schema change and see how &lt;code&gt;pgroll&lt;/code&gt; assists in managing both the old and new schema versions simultaneously.&lt;/p&gt;

&lt;p&gt;We'd like to change the &lt;code&gt;users&lt;/code&gt; table to disallow &lt;code&gt;NULL&lt;/code&gt; values in the &lt;code&gt;description&lt;/code&gt; field. We also want a &lt;code&gt;description&lt;/code&gt; to be set explicitly for all new users, so we will not set a default value for this column.&lt;/p&gt;

&lt;p&gt;There are two things that make this migration difficult:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We have existing &lt;code&gt;NULL&lt;/code&gt; values in our &lt;code&gt;description&lt;/code&gt; column that need to be updated to something not &lt;code&gt;NULL&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Existing applications using the table are still running and may be inserting more &lt;code&gt;NULL&lt;/code&gt; descriptions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;pgroll&lt;/code&gt; helps solve both problems by maintaining old and new versions of the schema side-by-side and transferring or modifying data between them as needed.&lt;/p&gt;

&lt;p&gt;Here is the &lt;code&gt;pgroll&lt;/code&gt; migration that will perform the migration to make the &lt;code&gt;description&lt;/code&gt; column &lt;code&gt;NOT NULL&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"02_user_description_set_nullable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"operations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"alter_column"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"table"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"column"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"nullable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"up"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"(SELECT CASE WHEN description IS NULL THEN 'description for ' || name ELSE description END)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"down"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"description"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save this migration as &lt;code&gt;sql/02_user_description_set_nullable.json&lt;/code&gt; and start the migration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pgroll start 02_user_description_set_nullable.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After some progress updates you'll receive a message confirming the successful start of the migration.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What's happening behind the progress updates?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In order to add the new &lt;code&gt;description&lt;/code&gt; column, &lt;code&gt;pgroll&lt;/code&gt; creates a temporary &lt;code&gt;_pgroll_new_description&lt;/code&gt; column and copies over the data from the existing &lt;code&gt;description&lt;/code&gt; column, using the &lt;code&gt;up&lt;/code&gt; SQL from the migration. As we have 10^5 rows in our table, this process takes some time. This process is called &lt;em&gt;backfilling&lt;/em&gt; and it is performed in batches to avoid locking all rows in the table simultaneously.&lt;/p&gt;


&lt;/blockquote&gt;

&lt;p&gt;At this point it's useful to look at the table data and schema to see what &lt;code&gt;pgroll&lt;/code&gt; has done. Let's look at the data first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see something 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;+-----+----------+-------------------------+--------------------------+
| id  | name     | description             | _pgroll_new_description  |
+-----+----------+-------------------------+--------------------------+
| 1   | user_1   | &amp;lt;null&amp;gt;                  | description for user_1   |
| 2   | user_2   | description for user_2  | description for user_2   |
| 3   | user_3   | &amp;lt;null&amp;gt;                  | description for user_3   |
| 4   | user_4   | description for user_4  | description for user_4   |
| 5   | user_5   | &amp;lt;null&amp;gt;                  | description for user_5   |
| 6   | user_6   | description for user_6  | description for user_6   |
| 7   | user_7   | &amp;lt;null&amp;gt;                  | description for user_7   |
| 8   | user_8   | &amp;lt;null&amp;gt;                  | description for user_8   |
| 9   | user_9   | description for user_9  | description for user_9   |
| 10  | user_10  | description for user_10 | description for user_10  |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the "expand" phase of the &lt;a href="https://openpracticelibrary.com/practice/expand-and-contract-pattern/" rel="noopener noreferrer"&gt;expand/contract pattern&lt;/a&gt; in action; &lt;code&gt;pgroll&lt;/code&gt; has added a &lt;code&gt;_pgroll_new_description&lt;/code&gt; field to the table and populated the field for all rows using the &lt;code&gt;up&lt;/code&gt; SQL logic from the &lt;code&gt;02_user_description_set_nullable.json&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"up"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"(SELECT CASE WHEN description IS NULL THEN 'description for ' || name ELSE description END)"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This has copied over all &lt;code&gt;description&lt;/code&gt; values into the &lt;code&gt;_pgroll_new_description&lt;/code&gt; field, rewriting any &lt;code&gt;NULL&lt;/code&gt; values using the provided SQL.&lt;/p&gt;

&lt;p&gt;Now let's look at the table schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DESCRIBE users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see something 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;+-------------------------+------------------------+-----------------------------------------------------------------+
| Column                  | Type                   | Modifiers                                                       |
|-------------------------+------------------------+-----------------------------------------------------------------|
| id                      | integer                |  not null default nextval('_pgroll_new_users_id_seq'::regclass) |
| name                    | character varying(255) |  not null                                                       |
| description             | text                   |                                                                 |
| _pgroll_new_description | text                   |                                                                 |
+-------------------------+------------------------+-----------------------------------------------------------------+
Indexes:
    "_pgroll_new_users_pkey" PRIMARY KEY, btree (id)
    "_pgroll_new_users_name_key" UNIQUE CONSTRAINT, btree (name)
Check constraints:
    "_pgroll_add_column_check_description" CHECK (_pgroll_new_description IS NOT NULL) NOT VALID
Triggers:
    _pgroll_trigger_users__pgroll_new_description BEFORE INSERT OR UPDATE ON users FOR EACH ROW EXECUTE FUNCTION _pgroll_trigger_users__pgroll_new_description()
    _pgroll_trigger_users_description BEFORE INSERT OR UPDATE ON users FOR EACH ROW EXECUTE FUNCTION _pgroll_trigger_users_description()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;_pgroll_new_description&lt;/code&gt; column has a &lt;code&gt;NOT NULL&lt;/code&gt; &lt;code&gt;CHECK&lt;/code&gt; constraint, but the old &lt;code&gt;description&lt;/code&gt; column is still nullable.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why is the &lt;code&gt;IS NOT NULL&lt;/code&gt; constraint on the new &lt;code&gt;_pgroll_new_description&lt;/code&gt; column &lt;code&gt;NOT VALID&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Defining the constraint as &lt;code&gt;NOT VALID&lt;/code&gt; means that the &lt;code&gt;users&lt;/code&gt; table will not be scanned to enforce the &lt;code&gt;NOT NULL&lt;/code&gt; constraint for existing rows. This means the constraint can be added quickly without locking rows in the table. &lt;code&gt;pgroll&lt;/code&gt; assumes that the &lt;code&gt;up&lt;/code&gt; SQL provided by the user will ensure that no &lt;code&gt;NULL&lt;/code&gt; values are written to the &lt;code&gt;_pgroll_new_description&lt;/code&gt; column.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;We'll talk about what the two triggers on the table do later.&lt;/p&gt;

&lt;p&gt;For now, let's look at the schemas in the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;\dn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see something 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;+-----------------------------------------+-------------------+
| Name                                    | Owner             |
+-----------------------------------------+-------------------+
| pgroll                                  | postgres          |
| public                                  | pg_database_owner |
| public_01_create_users_table            | postgres          |
| public_02_user_description_set_nullable | postgres          |
+-----------------------------------------+-------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have two schemas: one corresponding to the old schema, &lt;code&gt;public_01_create_users_table&lt;/code&gt;, and one for the migration we just started, &lt;code&gt;public_02_user_description_set_nullable&lt;/code&gt;. Each schema contains one view on the &lt;code&gt;users&lt;/code&gt; table. Let's look at the view in the first schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;\d+ public_01_create_users_table.users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output should contain something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;
   &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and for the second view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;\d+ public_02_user_description_set_nullable.users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output should contain something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_pgroll_new_description&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;
   &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second view exposes the same three columns as the first, but its &lt;code&gt;description&lt;/code&gt; field is mapped to the &lt;code&gt;_pgroll_new_description&lt;/code&gt; field in the underlying table.&lt;/p&gt;

&lt;p&gt;By choosing to access the &lt;code&gt;users&lt;/code&gt; table through either the &lt;code&gt;public_01_create_users_table.users&lt;/code&gt; or &lt;code&gt;public_02_user_description_set_nullable.users&lt;/code&gt; view, applications have a choice of which version of the schema they want to see; either the old version without the &lt;code&gt;NOT NULL&lt;/code&gt; constraint on the &lt;code&gt;description&lt;/code&gt; field or the new version with the constraint.&lt;/p&gt;

&lt;p&gt;When we looked at the schema of the &lt;code&gt;users&lt;/code&gt; table, we saw that &lt;code&gt;pgroll&lt;/code&gt; has created two triggers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;_pgroll_trigger_users__pgroll_new_description BEFORE INSERT OR UPDATE ON users FOR EACH ROW EXECUTE FUNCTION _pgroll_trigger_users__pgroll_new_description()
_pgroll_trigger_users_description BEFORE INSERT OR UPDATE ON users FOR EACH ROW EXECUTE FUNCTION _pgroll_trigger_users_description()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These triggers are used by &lt;code&gt;pgroll&lt;/code&gt; to ensure that any values written into the old &lt;code&gt;description&lt;/code&gt; column are copied over to the &lt;code&gt;_pgroll_new_description&lt;/code&gt; column (rewriting values using the &lt;code&gt;up&lt;/code&gt; SQL command from the migration) and to copy values written to the &lt;code&gt;_pgroll_new_description&lt;/code&gt; column back into the old &lt;code&gt;description&lt;/code&gt; column (rewriting values using the &lt;code&gt;down&lt;/code&gt; SQL command from the migration). Our migration did not specify any &lt;code&gt;down&lt;/code&gt; SQL command, so the default behaviour is just to copy data from the &lt;code&gt;_pgroll_new_description&lt;/code&gt; column into the &lt;code&gt;description&lt;/code&gt; column without modification.&lt;/p&gt;

&lt;p&gt;Let's see the first of those triggers in action.&lt;/p&gt;

&lt;p&gt;First set the &lt;a href="https://www.postgresql.org/docs/current/ddl-schemas.html#DDL-SCHEMAS-PATH" rel="noopener noreferrer"&gt;search path&lt;/a&gt; of the Postgres session to use the old schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;search_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'public_01_create_users_table'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now insert some data into the &lt;code&gt;users&lt;/code&gt; table through the &lt;code&gt;users&lt;/code&gt; view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Alice'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'this is Alice'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Bob'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This inserts two new users into the &lt;code&gt;users&lt;/code&gt; table, one with a &lt;code&gt;description&lt;/code&gt; and one without.&lt;/p&gt;

&lt;p&gt;Let's check that the data was inserted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Alice'&lt;/span&gt; &lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Bob'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running this query should show:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+--------+-------+---------------------+
| id     | name  | description         |
+--------+-------+---------------------+
| 100001 | Alice | this is Alice       |
| 100002 | Bob   | NULL                |
+--------+-------+---------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trigger should have copied the data that was just written into the old &lt;code&gt;description&lt;/code&gt; column (without the &lt;code&gt;NOT NULL&lt;/code&gt; constraint) into the &lt;code&gt;_pgroll_new_description&lt;/code&gt; column (with the &lt;code&gt;NOT NULL&lt;/code&gt; constraint) using the &lt;code&gt;up&lt;/code&gt; SQL from the migration.&lt;/p&gt;

&lt;p&gt;Let's check. Set the search path to the new version of the schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;search_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'public_02_user_description_set_nullable'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, find the users we just inserted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Alice'&lt;/span&gt; &lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Bob'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output should 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;+--------+-------+---------------------+
| id     | name  | description         |
+--------+-------+---------------------+
| 100001 | Alice | this is Alice       |
| 100002 | Bob   | description for Bob |
+--------+-------+---------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that the trigger installed by &lt;code&gt;pgroll&lt;/code&gt; has rewritten the &lt;code&gt;NULL&lt;/code&gt; value inserted into the old schema by using the &lt;code&gt;up&lt;/code&gt; SQL from the migration definition.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How do applications configure which version of the schema to use?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pgroll&lt;/code&gt; allows old and new versions of an application to exist side-by-side during a migration. Each version of the application should be configured with the name of the correct version schema, so that the application sees the database schema that it expects.&lt;/p&gt;

&lt;p&gt;This is done by setting the Postgres &lt;strong&gt;search_path&lt;/strong&gt; for the client's session and is described in more detail in the &lt;strong&gt;Client applications&lt;/strong&gt; section below.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Completing the migration
&lt;/h3&gt;

&lt;p&gt;Once the old version of the database schema is no longer required (for instance, the old applications that depend on the old schema are no longer running in production) the current migration can be completed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pgroll complete
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the migration has completed, the old version of the schema is no longer present in the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;\dn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;shows something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-----------------------------------------+-------------------+
| Name                                    | Owner             |
+-----------------------------------------+-------------------+
| pgroll                                  | postgres          |
| public                                  | pg_database_owner |
| public_02_user_description_set_nullable | postgres          |
+-----------------------------------------+-------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only the new version schema &lt;code&gt;public_02_user_description_set_nullable&lt;/code&gt; remains in the database.&lt;/p&gt;

&lt;p&gt;Let's look at the schema of the &lt;code&gt;users&lt;/code&gt; table to see what's changed there:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DESCRIBE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;shows something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-------------+------------------------+-----------------------------------------------------------------+----------+--------------+-------------+
| Column      | Type                   | Modifiers                                                       | Storage  | Stats target | Description |
+-------------+------------------------+-----------------------------------------------------------------+----------+--------------+-------------+
| id          | integer                |  not null default nextval('_pgroll_new_users_id_seq'::regclass) | plain    | &amp;lt;null&amp;gt;       | &amp;lt;null&amp;gt;      |
| name        | character varying(255) |  not null                                                       | extended | &amp;lt;null&amp;gt;       | &amp;lt;null&amp;gt;      |
| description | text                   |  not null                                                       | extended | &amp;lt;null&amp;gt;       | &amp;lt;null&amp;gt;      |
+-------------+------------------------+-----------------------------------------------------------------+----------+--------------+-------------+
Indexes:
    "_pgroll_new_users_pkey" PRIMARY KEY, btree (id)
    "_pgroll_new_users_name_key" UNIQUE CONSTRAINT, btree (name)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things have happened:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The extra &lt;code&gt;_pgroll_new_description&lt;/code&gt; has been renamed to &lt;code&gt;description&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The old &lt;code&gt;description&lt;/code&gt; column has been removed.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;description&lt;/code&gt; column is now marked as &lt;code&gt;NOT NULL&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The triggers to copy data back and forth between the old and new column have been removed.&lt;/li&gt;
&lt;/ul&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How is the column made &lt;code&gt;NOT NULL&lt;/code&gt; without locking?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because there is an existing &lt;code&gt;NOT NULL&lt;/code&gt; constraint on the column, created when the migration was started, making the column &lt;code&gt;NOT NULL&lt;/code&gt; when the migration is completed does not require a full table scan. See the Postgres &lt;a href="https://www.postgresql.org/docs/current/sql-altertable.html#SQL-ALTERTABLE-DESC-SET-DROP-NOT-NULL" rel="noopener noreferrer"&gt;docs&lt;/a&gt; for &lt;code&gt;SET NOT NULL&lt;/code&gt;.&lt;/p&gt;


&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;At this point, the migration is complete&lt;/strong&gt;. There is just one version schema in the database: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;public_02_user_description_set_nullable&lt;/code&gt; and the underlying &lt;code&gt;users&lt;/code&gt; table has the expected schema.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rollbacks
&lt;/h3&gt;

&lt;p&gt;The expand/contract approach to migrations means that the old version of the database schema (&lt;code&gt;01_create_users_table&lt;/code&gt; in this example) remains operational throughout the migration. This has two key benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Old versions of client applications that rely on the old schema continue to work.&lt;/li&gt;
&lt;li&gt;Rollbacks become trivial!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Looking at the second of these items, rollbacks, let's see how to roll back a &lt;code&gt;pgroll&lt;/code&gt; migration. We can start another migration now that our last one is complete:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"03_add_is_active_column"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"operations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"add_column"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"table"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"column"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"is_atcive"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"boolean"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"nullable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"true"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This migration adds a new column to the &lt;code&gt;users&lt;/code&gt; table. As before, we can start the migration with this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pgroll start 03_add_is_active_column.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once again, this creates a new version of the schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;\dn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Shows something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-----------------------------------------+-------------------+
| Name                                    | Owner             |
|-----------------------------------------+-------------------|
| pgroll                                  | postgres          |
| public                                  | pg_database_owner |
| public_02_user_description_set_nullable | postgres          |
| public_03_add_is_active_column          | postgres          |
+-----------------------------------------+-------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And adds a new column with a temporary name to the &lt;code&gt;users&lt;/code&gt; table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-----------------------+------------------------+-----------------------------------------------------------------+----------+--------------+-------------+
| Column                | Type                   | Modifiers                                                       | Storage  | Stats target | Description |
|-----------------------+------------------------+-----------------------------------------------------------------+----------+--------------+-------------|
| id                    | integer                |  not null default nextval('_pgroll_new_users_id_seq'::regclass) | plain    | &amp;lt;null&amp;gt;       | &amp;lt;null&amp;gt;      |
| name                  | character varying(255) |  not null                                                       | extended | &amp;lt;null&amp;gt;       | &amp;lt;null&amp;gt;      |
| description           | text                   |  not null                                                       | extended | &amp;lt;null&amp;gt;       | &amp;lt;null&amp;gt;      |
| _pgroll_new_is_atcive | boolean                |  default true                                                   | plain    | &amp;lt;null&amp;gt;       | &amp;lt;null&amp;gt;      |
+-----------------------+------------------------+-----------------------------------------------------------------+----------+--------------+-------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new column is not present in the view in the old version of the schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;\d+ public_02_user_description_set_nullable.users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Shows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; SELECT users.id,
    users.name,
    users.description
   FROM users;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But is exposed by the new version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;\d+ public_03_add_is_active_column.user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Shows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; SELECT users.id,
    users.name,
    users.description,
    users._pgroll_new_is_atcive AS is_atcive
   FROM users;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, there's a typo in the column name: &lt;code&gt;isAtcive&lt;/code&gt; instead of &lt;code&gt;isActive&lt;/code&gt;. The migration needs to be rolled back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pgroll rollback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rollback has removed the old version of the schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-----------------------------------------+-------------------+
| Name                                    | Owner             |
|-----------------------------------------+-------------------|
| pgroll                                  | postgres          |
| public                                  | pg_database_owner |
| public_02_user_description_set_nullable | postgres          |
+-----------------------------------------+-------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the new column has been removed from the underlying table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-------------+------------------------+-----------------------------------------------------------------+----------+--------------+-------------+
| Column      | Type                   | Modifiers                                                       | Storage  | Stats target | Description |
|-------------+------------------------+-----------------------------------------------------------------+----------+--------------+-------------|
| id          | integer                |  not null default nextval('_pgroll_new_users_id_seq'::regclass) | plain    | &amp;lt;null&amp;gt;       | &amp;lt;null&amp;gt;      |
| name        | character varying(255) |  not null                                                       | extended | &amp;lt;null&amp;gt;       | &amp;lt;null&amp;gt;      |
| description | text                   |  not null                                                       | extended | &amp;lt;null&amp;gt;       | &amp;lt;null&amp;gt;      |
+-------------+------------------------+-----------------------------------------------------------------+----------+--------------+-------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since the original schema version, &lt;code&gt;02_user_description_set_nullable&lt;/code&gt;, was never removed, existing client applications remain unaware of the migration and subsequent rollback.&lt;/p&gt;

&lt;h3&gt;
  
  
  Client applications
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;pgroll&lt;/code&gt; uses the &lt;a href="https://openpracticelibrary.com/practice/expand-and-contract-pattern/" rel="noopener noreferrer"&gt;expand/contract pattern&lt;/a&gt; to roll out schema changes. Each migration creates a new version schema in the database.&lt;/p&gt;

&lt;p&gt;In order to work with the multiple versioned schema that &lt;code&gt;pgroll&lt;/code&gt; creates, clients need to be configured to work with one of them.&lt;/p&gt;

&lt;p&gt;This is done by having client applications configure the &lt;a href="https://www.postgresql.org/docs/current/ddl-schemas.html#DDL-SCHEMAS-PATH" rel="noopener noreferrer"&gt;search path&lt;/a&gt; when they connect to the Postgres database.&lt;/p&gt;

&lt;p&gt;For example, this fragment for a Go client application shows how to set the &lt;code&gt;search_path&lt;/code&gt; after a connection is established:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"postgres"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;searchPath&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"02_user_description_set_nullable"&lt;/span&gt;
&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SET search_path = %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QuoteIdentifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;searchPath&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed to set search path: %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In practice, the &lt;code&gt;searchPath&lt;/code&gt; variable would be provided to the application as an environment variable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get involved
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;pgroll&lt;/code&gt; is an open source project, and we're really excited to see more people getting involved. If you're interested in submitting issues, giving feedback, or contributing pull requests, our &lt;a href="https://github.com/xataio/pgroll" rel="noopener noreferrer"&gt;repository&lt;/a&gt; is the place to be!&lt;/p&gt;

&lt;p&gt;If you want to discuss further, find out more about projects at Xata, or just say hi, join us on &lt;a href="https://xata.io/discord" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; or follow us on &lt;a href="https://twitter.com/xata" rel="noopener noreferrer"&gt;X | Twitter&lt;/a&gt;. We're always ready to chat, answer questions, and keep you in the loop with the latest from Xata. We look forward to your input and ideas!&lt;/p&gt;

</description>
      <category>schema</category>
      <category>postgres</category>
      <category>database</category>
      <category>postgressql</category>
    </item>
    <item>
      <title>Announcing the release of the Xata Go SDK</title>
      <dc:creator>Joan</dc:creator>
      <pubDate>Thu, 07 Dec 2023 07:00:00 +0000</pubDate>
      <link>https://dev.to/xata/announcing-the-release-of-the-xata-go-sdk-47c4</link>
      <guid>https://dev.to/xata/announcing-the-release-of-the-xata-go-sdk-47c4</guid>
      <description>&lt;p&gt;Earlier this year we shared a &lt;a href="https://xata.io/blog/community-spotlight-xata-go-sdk" rel="noopener noreferrer"&gt;community spotlight&lt;/a&gt; focused on a Go SDK developed by a dedicated contributor, &lt;a href="https://github.com/kerdokurs/xata-go" rel="noopener noreferrer"&gt;xata-go&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/kerdokurs/" rel="noopener noreferrer"&gt;Kerdo Kurs&lt;/a&gt;, the developer behind this project, was a huge inspiration for us committing to an official Go SDK. Today, we're thrilled to announce the official release of our Xata Go SDK, and open the doors to the growing Go community 🎉&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;v0.0.1&lt;/code&gt; release features our most popular endpoints, enabling functionalities like searching a branch, using the ask endpoint for follow-up questions, and creating a database, among others. You can see the evolving endpoint coverage in the ticket &lt;a href="https://github.com/xataio/xata-go/issues/1" rel="noopener noreferrer"&gt;xata-go#1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to get started right away, head over to the Go SDK &lt;a href="https://xata.io/docs/sdk/go/overview" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; and browse the code on &lt;a href="https://github.com/xataio/xata-go" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In our documentation, we've included examples showing how to use the Go SDK, similar to the existing guides we have for TypeScript, Python, cURL, and SQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Anatomy of the SDK
&lt;/h2&gt;

&lt;p&gt;In the Go SDK, components are organized into specialized clients within distinct namespaces, like the &lt;code&gt;RecordsClient&lt;/code&gt; for record-related functions.&lt;/p&gt;

&lt;p&gt;All endpoints that are listed in our &lt;a href="https://xata.io/docs/api-reference/db/db_branch_name/transaction" rel="noopener noreferrer"&gt;API reference&lt;/a&gt;, are modeled within this client.&lt;/p&gt;

&lt;p&gt;The available clients are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;BranchClient&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DatabasesClient&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FilesClient&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RecordsClient&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SearchAndFilterClient&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TableClient&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UsersClient&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WorkspaceClient&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code and corresponding test suites can be found in the &lt;a href="https://github.com/xataio/xata-go/tree/main/xata" rel="noopener noreferrer"&gt;xata package&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you're familiar with our SDKs, you'll notice familiar API naming patterns, adapted to align with Go's idiomatic style.&lt;br&gt;
To illustrate the usage of the Go SDK, we curated a couple of examples. Browse &lt;a href="https://xata.io/docs/sdk/get" rel="noopener noreferrer"&gt;our docs&lt;/a&gt; to see the full bandwidth of examples.&lt;/p&gt;

&lt;p&gt;The following example succinctly demonstrates, how to use the Ask endpoint.&lt;/p&gt;

&lt;p&gt;For demo purposes we assume that the table &lt;code&gt;IMDB&lt;/code&gt; contains all movies listed from &lt;a href="https://www.imdb.com/" rel="noopener noreferrer"&gt;IMDB.com&lt;/a&gt; and we want to learn how many Ace Ventura movies exist.&lt;/p&gt;

&lt;p&gt;More complex queries and follow up questions examples can be found &lt;a href="https://xata.io/docs/sdk/ask" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;searchClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;xata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewSearchAndFilterClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;searchClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TODO&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;xata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AskRequest&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;TableName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"IMDB"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Question&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="s"&gt;"How many Ace Ventura movies are there?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we have not normalized data in multiple tables in one branch and want to search across this entire branch.&lt;/p&gt;

&lt;p&gt;We are looking for the name &lt;em&gt;Philip&lt;/em&gt;, but as there are multiple ways of writing Philip (such as &lt;em&gt;Filip&lt;/em&gt; or &lt;em&gt;Phillip&lt;/em&gt; or &lt;em&gt;Philippe&lt;/em&gt;) we need to apply fuzziness to our search. &lt;code&gt;Fuzziness: xata.Int(2)&lt;/code&gt; sets the search fuzziness level to a degree of 2, allowing for slight variations in the spelling of search terms.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;searchClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;xata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewSearchAndFilterClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;searchClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SearchBranch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TODO&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;xata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SearchBranchRequest&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Payload&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;xata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SearchBranchRequestPayload&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="s"&gt;"Philip"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Fuzziness&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;xata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last example illustrates the use of the &lt;a href="https://xata.io/docs/sdk/transaction" rel="noopener noreferrer"&gt;transaction&lt;/a&gt; endpoint.&lt;br&gt;
Transactions are a powerful way to do multiple operations in one go.&lt;/p&gt;

&lt;p&gt;For the sake of the example, we solely want to create multiple records of actors that participated in the movie the Matrix.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;recordsClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;xata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRecordsClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;recordsClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TODO&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;xata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TransactionRequest&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Operations&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;xata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TransactionOperation&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;xata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewInsertTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TransactionInsertOp&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;Table&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Actors"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;xata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Keanu Charles Reeves"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s"&gt;"movie"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;xata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"the_matrix"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="n"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;xata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Carrie-Anne Moss"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s"&gt;"movie"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;xata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"the_matrix"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="n"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;xata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Laurence Fishburne"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s"&gt;"movie"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;xata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"the_matrix"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What’s next?
&lt;/h2&gt;

&lt;p&gt;The SDK is an alpha release, and not all &lt;a href="https://github.com/xataio/xata-go/issues/1" rel="noopener noreferrer"&gt;endpoints are covered&lt;/a&gt;, we continue to increase the API coverage of the SDK and provide bug fixes.&lt;/p&gt;

&lt;p&gt;We can't do it without you! If you want to contribute &lt;a href="https://github.com/xataio/xata-go" rel="noopener noreferrer"&gt;code&lt;/a&gt;, add an &lt;a href="https://github.com/xataio/xata-go/tree/main/examples" rel="noopener noreferrer"&gt;example&lt;/a&gt; update the &lt;a href="https://xata.io/docs/sdk/go/overview" rel="noopener noreferrer"&gt;docs&lt;/a&gt;, or found a bug please open a PR or issue and we will assist you. All contributions are welcome ✅&lt;/p&gt;

&lt;p&gt;To stay up to date with that latest at Xata, follow us on &lt;a href="https://twitter.com/xata" rel="noopener noreferrer"&gt;X | Twitter&lt;/a&gt; or pop in and say "hi" 👋 in &lt;a href="https://xata.io/discord" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Is there a language you wished we supported natively but don't today? Feel free to open up a &lt;a href="https://xata.canny.io/feature-requests" rel="noopener noreferrer"&gt;feature request&lt;/a&gt; or check-in with our amazing community.&lt;/p&gt;

</description>
      <category>go</category>
      <category>database</category>
      <category>developer</category>
      <category>sdk</category>
    </item>
    <item>
      <title>What is Horizontal Sharding?</title>
      <dc:creator>Joan</dc:creator>
      <pubDate>Wed, 06 Dec 2023 23:35:40 +0000</pubDate>
      <link>https://dev.to/xata/what-is-horizontal-sharding-111i</link>
      <guid>https://dev.to/xata/what-is-horizontal-sharding-111i</guid>
      <description>&lt;p&gt;Recently, while wandering through the narrow aisles of a small comic store brimming with pop culture relics, I came across an interesting find. Hidden amongst a varied assortment of DC and Marvel comics, were shelves dedicated to the "Jodoverse" - a series of comics created by Alejandro Jodorowsky. With the spines showing slight signs of age, I remarked to the store owner about the find and asked for a specific issue. Without resorting to a system, ledger, or anything, he quickly scanned the shelves and in a matter of milliseconds found exactly what I asked for with no hassle. I was impressed to see that his collection was organized by genre, collaborator, publication year as well as story arc!&lt;/p&gt;

&lt;p&gt;The store owner's organizational strategy allowed him to quickly access comics. His store was divided into different sections; each section was like a mini comic store specializing in a particular category. For example, if you were looking for a 90s Jodorowsky comic created in collaboration with a particular artist, you'd first go to the section dedicated to Jodorowsky's works, find a specific area for collaborations, and then narrow your search down to the shelves tagged with the 90s. This setup, where each 'mini-store' or section held a distinct subset of the entire collection, mirrors the concept of horizontal sharding in databases, where data is split into manageable parts based on specific keys for more efficient access and retrieval.&lt;/p&gt;

&lt;p&gt;Just as the comics are grouped and allocated to specific shelves based on various attributes, horizontal sharding distributes rows of a database across multiple locations, or "shards", based on specific key attributes. This method not only ensures quick access but also optimizes storage and performance. The store owner knew exactly where each comic belonged; the same with horizontal sharding which ensures that every piece of data finds its right place in a database system.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Sharding
&lt;/h2&gt;

&lt;p&gt;Sharding is a database design technique where data is split across multiple servers, or "shards", each holding a portion of the data. Think of it like the shop mentioned above: instead of having everything jumbled together, the shop is organized into sections, with each section (or "shard") holding a specific category of comics. This structure makes it easier and faster to find a particular item, just as sharding can make databases more efficient by spreading the load across multiple servers.&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%2Fejyyo8gdxrvzstczpfds.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%2Fejyyo8gdxrvzstczpfds.gif" alt="Separate" width="360" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As data volumes grow exponentially, efficient data management becomes extremely important. That's why sharding is so important, it's one of the techniques employed to maintain the sanity and speed of large databases. So, what are the different types of sharding?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Vertical sharding&lt;/strong&gt;: In this approach, different tables or columns are placed on different servers. For example, one server might store names and descriptions, while another might store order histories.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Horizontal sharding&lt;/strong&gt;: This is the most common form of sharding. Here, rows of a database table are held separately, rather than splitting by columns. For instance, if you had a database of comics, one shard might contain issue IDs 1 to 1000, while another might hold 1001 to 2000, and so on.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Range-based sharding&lt;/strong&gt;: Range-based sharding is a specific method of horizontal sharding where rows are partitioned across shards based on ranges of values of a sharding key. For instance, user IDs 1 to 1000 might be stored on one shard, while user IDs 1001 to 2000 might be on another. In essence, all range-based sharding is horizontal sharding, but not all horizontal sharding is range-based. Other horizontal sharding strategies might involve hashing, directory-based sharding, or even geographically-based sharding. Each strategy has its own advantages and best-use cases.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&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%2Fgvpkx77z35nz750wtmdj.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%2Fgvpkx77z35nz750wtmdj.gif" alt="And" width="480" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Other types of sharding exist and include:&lt;/p&gt;

&lt;h4&gt;
  
  
  Hash-based sharding
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;A hash function is applied to a key attribute, and the result determines which shard the record goes to. This type of sharding may be good for evenly distributing data, especially when the range or list is not predictable.&lt;/li&gt;
&lt;li&gt;For example, user IDs can be hashed, and the hash value determines the shard.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Directory-based sharding
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;A separate lookup service or directory maps each data item to a specific shard. This offers flexibility in sharding and can handle more complex data distribution scenarios. &lt;/li&gt;
&lt;li&gt;For example, a mapping service can direct user queries to the appropriate shard based on the user's location or preferences.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
   Geo-sharding
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Data is sharded based on geographical locations. This is useful for services that need to reduce latency by locating data closer to the user. &lt;/li&gt;
&lt;li&gt;For example, social media posts might be stored in a shard located in the same region as the user.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Key-based sharding
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;This is also known as customer sharding. The sharding is done based on a specific key, such as customer ID or tenant ID. This can be used for multi-tenant applications where data isolation between tenants is crucial. &lt;/li&gt;
&lt;li&gt;For example, each comic's data can be stored in a separate shard.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Functional sharding
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;This is sharding based on the function or the type of data. This is suitable for systems where different types of data have vastly different access patterns. &lt;/li&gt;
&lt;li&gt;For example, this can be used when one shard handles all the transactional
data, while another handles logging or archival data.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Sharding comparison example
&lt;/h3&gt;

&lt;p&gt;In horizontal sharding, each shard contains a subset of the rows from the original table, but the structure (columns) remains the same across all shards.&lt;/p&gt;

&lt;p&gt;Suppose there is a table of Marvel comic book issues:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue ID&lt;/th&gt;
&lt;th&gt;Series Title&lt;/th&gt;
&lt;th&gt;Character&lt;/th&gt;
&lt;th&gt;Release Date&lt;/th&gt;
&lt;th&gt;Storyline&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To shard this table horizontally based on the series title, it might look like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shard 1 (Avengers series)&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue ID&lt;/th&gt;
&lt;th&gt;Series Title&lt;/th&gt;
&lt;th&gt;Character&lt;/th&gt;
&lt;th&gt;Release Date&lt;/th&gt;
&lt;th&gt;Storyline&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;001&lt;/td&gt;
&lt;td&gt;Avengers&lt;/td&gt;
&lt;td&gt;Iron Man&lt;/td&gt;
&lt;td&gt;2018-05-01&lt;/td&gt;
&lt;td&gt;Story A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;002&lt;/td&gt;
&lt;td&gt;Avengers&lt;/td&gt;
&lt;td&gt;Thor&lt;/td&gt;
&lt;td&gt;2018-06-01&lt;/td&gt;
&lt;td&gt;Story B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Shard 2 (Spider-Man series)&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue ID&lt;/th&gt;
&lt;th&gt;Series Title&lt;/th&gt;
&lt;th&gt;Character&lt;/th&gt;
&lt;th&gt;Release Date&lt;/th&gt;
&lt;th&gt;Storyline&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;Spider-Man&lt;/td&gt;
&lt;td&gt;Spider-Man&lt;/td&gt;
&lt;td&gt;2019-05-01&lt;/td&gt;
&lt;td&gt;Story X&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;Spider-Man&lt;/td&gt;
&lt;td&gt;Spider-Man&lt;/td&gt;
&lt;td&gt;2019-06-01&lt;/td&gt;
&lt;td&gt;Story Y&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is represented in the below diagram:&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%2Fjzqmap6le6xpa5sgsaa2.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%2Fjzqmap6le6xpa5sgsaa2.png" alt="Horizontal sharding" width="791" height="775"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Conversely, in &lt;strong&gt;&lt;em&gt;vertical sharding&lt;/em&gt;&lt;/strong&gt;, each shard contains a subset of the columns from the original table.&lt;/p&gt;

&lt;p&gt;Using the same original table structure, if divided into two shards with &lt;strong&gt;&lt;em&gt;vertical sharding&lt;/em&gt;&lt;/strong&gt;, it might look something like the following:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shard 1 (Basic information)&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue ID&lt;/th&gt;
&lt;th&gt;Series Title&lt;/th&gt;
&lt;th&gt;Release Date&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;001&lt;/td&gt;
&lt;td&gt;Avengers&lt;/td&gt;
&lt;td&gt;2018-05-01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;002&lt;/td&gt;
&lt;td&gt;Spider-Man&lt;/td&gt;
&lt;td&gt;2019-05-01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Shard 2 (Content details)&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue ID&lt;/th&gt;
&lt;th&gt;Character&lt;/th&gt;
&lt;th&gt;Storyline&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;001&lt;/td&gt;
&lt;td&gt;Iron Man&lt;/td&gt;
&lt;td&gt;Story A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;002&lt;/td&gt;
&lt;td&gt;Spider-Man&lt;/td&gt;
&lt;td&gt;Story X&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In horizontal sharding, the data is divided into different rows across shards, but the table structure (columns) is the same. In vertical sharding, the data is divided into different columns across shards, potentially splitting the table into different aspects or types of data.&lt;/p&gt;




&lt;h3&gt;
  
  
  Why horizontal sharding?
&lt;/h3&gt;

&lt;p&gt;Database sharding is important for several reasons, primarily relating to the scalability, performance, and reliability of database systems, especially when managing massive amounts of data. Here's why it's considered crucial:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scalability&lt;/strong&gt;: As databases grow in size, they can experience slowdowns in query performance, potentially affecting the user experience for applications that depend on them. Sharding helps databases scale out by distributing the data across multiple servers or clusters, enabling them to handle larger workloads and more users.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Improved performance&lt;/strong&gt;: Distributing the data across multiple servers means that fewer rows are queried in each shard. This leads to faster query performance since each server has less data to sift through.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Load balancing&lt;/strong&gt;: Sharding can distribute the database load evenly across servers. This ensures that no single server becomes a bottleneck, improving the overall performance and responsiveness of the database system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hardware cost efficiency&lt;/strong&gt;: Instead of scaling up, sharding allows organizations to scale out, adding more servers to the mix. This spreads data across multiple servers, each handling a part of the total data, which lightens the load on individual servers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Failover and redundancy&lt;/strong&gt;: If one shard fails, it won't bring down the entire database. Only the users or transactions tied to that specific shard would be affected, while others can continue operations normally. This can be combined with replication strategies to provide even better fault tolerance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Geographical distribution&lt;/strong&gt;: For global applications, sharding can be done based on geographical considerations. This means that users can be served from a nearby shard, reducing latency and improving application responsiveness.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Isolation of problematic workloads&lt;/strong&gt;: If a particular shard starts to experience issues, it can be isolated and managed without affecting the performance of other shards. This isolation can also be good for maintenance and updates.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Storage management&lt;/strong&gt;: Different shards can be placed on different storage mediums depending on the access patterns.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&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%2F0tnupqryad52ltk2ulq5.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%2F0tnupqryad52ltk2ulq5.gif" alt="Run" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits of horizontal sharding
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Handles more data and users&lt;/strong&gt;: Sharding lets a database grow and handle more data and users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speeds up searches&lt;/strong&gt;: It's usually quicker to find something in a smaller room than a huge library. Similarly, sharding makes data searches faster.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keeps things running&lt;/strong&gt;: If one shard has a problem, the others keep working.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Challenges of horizontal sharding
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Efficient organization&lt;/strong&gt;: Think of it as sorting comics by themes and years in the comic store. In database sharding, the challenge is to efficiently organize data across shards, ensuring it's easy to manage and retrieve without getting jumbled up.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Consistency across sections&lt;/strong&gt;: Just like maintaining different sections in the store, managing shards can lead to inconsistencies in data retrieval. Users accessing different shards might experience inconsistent query performance, similar to how different sections in the store can offer varying experiences. In sharding, data integrity can become a concern, especially when managing shards independently. Ensuring consistency and integrity across shards is vital to prevent data issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Balancing workload&lt;/strong&gt;: Imagine one section of the comic store becoming too popular, causing overcrowding, while others remain empty. In sharding, this relates to load balancing. Uneven data distribution can overload some shards, resulting in performance issues, similar to a crowded section causing chaos.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Handling historical data&lt;/strong&gt;: Just as we talked about those older comic books, database sharding also involves the challenge of managing historical data. As data accumulates over time, adopting efficient strategies to handle this older information becomes essential to sustain both performance and storage effectiveness.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performance bottlenecks&lt;/strong&gt;: An overcrowded store sections lead to slower service, improper shard distribution can cause performance bottlenecks in database sharding. Ensuring optimal data distribution among shards is essential to avoid these performance issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Complexity management&lt;/strong&gt;: Similar to the comic store's categorization system, managing multiple shards in a database can be challenging. Implementing and maintaining sharding strategies require careful planning to avoid complexity, in addition to managing costs associated with adding more servers or clusters for sharding can be a challenge. Also, PostgreSQL itself does not provide built-in database sharding in the same way some distributed databases do. However, PostgreSQL supports table partitioning, which can be seen as a form of horizontal sharding at the table level.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&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%2F5xuwb87caswqzffpcs0y.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%2F5xuwb87caswqzffpcs0y.gif" alt="Not Easy" width="480" height="268"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Implementing horizontal sharding
&lt;/h2&gt;

&lt;p&gt;Horizontal sharding is not just a method of splitting a database; it's a strategic approach to reorganize how data is stored and accessed. Each phase, from choosing the sharding key to managing data across multiple shards, plays an important role in making the database more scalable and efficient. When done correctly, horizontal sharding can significantly improve a database's performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Selecting a sharding key&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The process starts with choosing a sharding key. This critical step involves identifying a specific data attribute that will dictate how the database is divided into shards. The success of sharding largely depends on this choice, as it affects how easily data can be accessed and managed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Shard creation and management&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After determining the sharding key, the next step is to create the actual shards. These are smaller portions of the original database, designed to improve efficiency. However, it's not just about creating these shards; managing them is equally important. This includes tasks like ensuring data is evenly distributed across shards and that each shard is performing well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Data migration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Migrating existing data to the newly created shards is a delicate operation. It requires careful planning to make sure that the data fits well with the chosen sharding key. Proper execution of this step is vital to ensure that data is distributed correctly across the shards.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Handling queries across shards&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sometimes, queries need to pull data from multiple shards. Handling these queries efficiently is crucial. The aim is to keep the retrieval process quick and smooth, maintaining the benefits of having a sharded database.&lt;/p&gt;




&lt;h3&gt;
  
  
  Horizontal sharding in Postgres
&lt;/h3&gt;

&lt;p&gt;To get sharding in PostgreSQL, it usually has to be set up manually. This means creating several Postgres instances where each one acts like a separate piece of the larger database. Deciding how to split the data across these pieces involves choosing a key, like user IDs or locations. This process requires a lot of initial setup and ongoing work to keep it running smoothly.&lt;/p&gt;

&lt;p&gt;There are also &lt;a href="https://pgxn.org/dist/pg_shard/pg_shard.html#:~:text=pg_shard%20is%20a%20sharding%20extension,any%20changes%20to%20your%20application." rel="noopener noreferrer"&gt;extensions&lt;/a&gt; available that can help with sharding in PostgreSQL. These tools make it easier for PostgreSQL to handle data spread out across different places, making the database more scalable. However, adding sharding, whether by doing it manually or using these tools, makes the database system more complex. It requires careful planning and regular maintenance, especially when dealing with big datasets.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tips for horizontal sharding
&lt;/h2&gt;

&lt;p&gt;Implementing horizontal sharding in database management involves several key strategies. Below is a summary:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sharding key&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Choose based on frequent data access to evenly split data.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Balancing data&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Keep shards evenly loaded and adjust regularly to prevent overloading.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Speedy queries&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Optimize queries across shards for speed using efficient algorithms.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Complex transactions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Use protocols to maintain consistent data across all shards.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Consistency checks&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Regularly sync and check data for accuracy across shards.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Adaptable sharding&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Design sharding to easily grow and adapt to changing data needs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Managing shards&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Monitor shard performance and automate tasks for efficiency.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Backup plans&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Implement strong backup and recovery strategies for each shard.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resource management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Smartly plan and use resources, utilizing cloud services for flexibility.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Shard security&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Protect data in each shard with encryption and strict access control.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;And there we go – a basic rundown of horizontal sharding in databases. This overview touches just the tip of the iceberg, but it gives some insight into how horizontal sharding helps in managing large datasets more efficiently.&lt;/p&gt;

&lt;p&gt;Let us know what you think. We have a lot more to say on the topic, so reach out to us on &lt;a href="https://xata.io/discord" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; or follow us on &lt;a href="https://twitter.com/xata" rel="noopener noreferrer"&gt;X | Twitter&lt;/a&gt;. We'd love to hear your thoughts, answer your questions, and keep you updated on the latest at &lt;a href="https://xata.io/" rel="noopener noreferrer"&gt;Xata&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>database</category>
      <category>beginners</category>
      <category>postgres</category>
      <category>learning</category>
    </item>
    <item>
      <title>Using Next.js to improve speed and efficiency</title>
      <dc:creator>Joan</dc:creator>
      <pubDate>Tue, 14 Nov 2023 11:14:56 +0000</pubDate>
      <link>https://dev.to/xata/using-nextjs-to-improve-speed-and-efficiency-3gbj</link>
      <guid>https://dev.to/xata/using-nextjs-to-improve-speed-and-efficiency-3gbj</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The following post highlights some of the insights and experiences our Xata's Senior Product Designer, Elizabet Oliveira, shared at a recent talk at Next.js Conf 2023&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/2c8T4w1Uudg"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;In the ever-evolving landscape of tech and startups, the journey from inception to success can be compared to Buzz Lightyear's famous catchphrase: "To infinity and beyond!" The possibilities are limitless, but the challenges can be overwhelming. Fast-growing startups often find themselves grappling with the demands of rapid scaling, talent acquisition, market competition, and the relentless pressure to innovate. Yet, in this quest for excellence, there's an unexpected roadblock – web app fragmentation.&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%2F18ff2nziunztmmkenlmo.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%2F18ff2nziunztmmkenlmo.png" alt="To Infinity" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Web app fragmentation
&lt;/h2&gt;

&lt;p&gt;Web app fragmentation occurs when startups, trying to overcome these challenges, end up using various tech tools for different tasks. For instance, Webflow might be their choice for the website, Docusaurus for documentation, React for their dashboard, and the list goes on. This approach may lead to a disjointed and inconsistent user experience.&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%2Fvjsa9h3fefvro97qs2dq.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%2Fvjsa9h3fefvro97qs2dq.png" alt="Web app example" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Navigating startup success with Next.js
&lt;/h2&gt;

&lt;p&gt;Success with Next.js for startups involves selecting tools that support efficient growth and ensure that the product development aligns with startup goals.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unifying the tech stack for consistency
&lt;/h3&gt;

&lt;p&gt;Web application fragmentation often poses a considerable obstacle for emerging companies. At &lt;a href="https://xata.io/" rel="noopener noreferrer"&gt;Xata&lt;/a&gt;, we've faced and addressed this dilemma by standardizing our technological tools through the implementation of &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;. Opting for this approach has brought a range of benefits, including simplified maintenance, efficient development, improved SEO, and overall a consistent user experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Beyond unifying our tech stack
&lt;/h3&gt;

&lt;p&gt;We've looked into several factors that have significantly contributed to our progress.&lt;/p&gt;

&lt;h4&gt;
  
  
  The role of a design system
&lt;/h4&gt;

&lt;p&gt;A design system, guided by clear standards and comprising reusable components, is very important. We've implemented a design system using Chakra UI and custom tokens, which promotes accessibility and semantic consistency.&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%2F3jcmhca3qs793rbycobe.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%2F3jcmhca3qs793rbycobe.png" alt="Design systems" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using this approach ensures that different products have a similar design because it relies on reusing the same building blocks and style elements.&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%2Fw6nag094m2qipy0puoqi.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%2Fw6nag094m2qipy0puoqi.png" alt="COnsistency" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Enhancing SEO with Next.js
&lt;/h4&gt;

&lt;p&gt;Effective SEO practices draw in users. Next.js helped us improve SEO with its server-side rendering feature, which creates previews of content that are optimized for social media, and it also provides easy ways to manage meta tags. These features help search engines better understand and index content, potentially leading to higher rankings.&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%2Fqgg0849hrb8nz11slwe6.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%2Fqgg0849hrb8nz11slwe6.png" alt="Tweet preview" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Leveraging analytics and monitoring
&lt;/h4&gt;

&lt;p&gt;For startups, analytics, and monitoring are essential for tracking user behavior, troubleshooting issues, and making data-driven decisions. We use Next.js and Vercel to conduct A/B tests, which allows us to make informed choices for our startup.&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%2Fyxj6yd6mcugoj4ukb5ai.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%2Fyxj6yd6mcugoj4ukb5ai.png" alt="Testing" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using Next.js, we can easily connect 3rd party analytics tools and use built-in performance monitoring. This means we can keep an eye on how fast our website loads and how quickly people can see our content when they visit.&lt;/p&gt;

&lt;h4&gt;
  
  
  A thriving community for startups
&lt;/h4&gt;

&lt;p&gt;The Next.js community and ecosystem provide invaluable resources for startups. One particularly noteworthy plugin is Contentlayer, a content SDK that simplifies the validation and transformation of your content into type-safe JSON data, which can be seamlessly imported into applications. This plugin has been a game-changer for us at Xata, making the process of building our documentation and blog significantly easier.&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%2F0qh0oow5zmsj84fw3ihd.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%2F0qh0oow5zmsj84fw3ihd.png" alt="Content layer" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Powering growth with Next.js and Vercel
&lt;/h4&gt;

&lt;p&gt;Combining Next.js and Vercel supercharges performance and development speed. We use Vercel features such as preview deployments, templates, starters, and themes. As a team of two designers with coding skills at Xata, we design directly with Next.js and gather feedback through the Vercel commenting system.&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%2Fgy14tqix6ab7imegdui8.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%2Fgy14tqix6ab7imegdui8.png" alt="Vercel comments" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Creating a template for a product compatible with Next.js is an effective method for making it accessible to users.&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%2Flx5qmqdssi2r18t6dk5t.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%2Flx5qmqdssi2r18t6dk5t.png" alt="Xata template" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;Wrapping up, startups can typically grapple with several challenges, one of the most common being the fragmentation of web applications. This can result in a confusing experience for users in which Next.js can be a great solution, offering a way to consolidate different technologies and create a cohesive tech stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;By using a consistent design system, better interaction for users across various platforms can be achieved.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Leveraging Next.js for search engine optimization boosts a site’s discoverability and increases organic reach.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Integrated analytics and monitoring provide invaluable data, enabling informed decision-making.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The supportive Next.js community and its rich ecosystem contribute to a more efficient development cycle.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ultimately, making informed technology choices are pivotal for a startup's journey towards success, and Next.js can be a key part of that toolkit. If you're at a startup and juggling various tech stacks, consider trying Next.js. You can learn more from our session at Next.js Conf 2023, which you can watch by clicking here.&lt;/p&gt;

&lt;p&gt;So, are you facing tech hurdles like we did or do you have something to share? Contribute to our community; we'd love to hear from you. Tell us on &lt;a href="https://discord.com/invite/kvAcQKh7vm" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; or &lt;a href="https://twitter.com/xata" rel="noopener noreferrer"&gt;X | Twitter&lt;/a&gt; and happy coding! 🦋&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>devops</category>
      <category>developer</category>
      <category>community</category>
    </item>
    <item>
      <title>Database Horror Stories</title>
      <dc:creator>Joan</dc:creator>
      <pubDate>Tue, 31 Oct 2023 12:02:47 +0000</pubDate>
      <link>https://dev.to/xata/database-horror-stories-5ba6</link>
      <guid>https://dev.to/xata/database-horror-stories-5ba6</guid>
      <description>&lt;p&gt;Halloween brings to mind ghosts and goblins, but for developers, the true frights can often lie hidden in their tech stack. Beneath the surface of applications, databases hold their own unsettling tales.&lt;/p&gt;

&lt;p&gt;In this post, we are looking at some of the terrors that can adversely impact your database project, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The dread of failed backups&lt;/li&gt;
&lt;li&gt;Spooky schema migrations&lt;/li&gt;
&lt;li&gt;Vampiric vacuums that suck the life out of you and your database&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Boogeyman backups: Nightmares of lost data
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;As midnight neared, under the eerie glow of her computer monitor, the developer's small room was awash in a chilling light. Her screen flashed an ominous message: "Backup Failed.” A cold dread settled in the pit of her stomach. To her horror, she realized that months of critical data and work had vanished into the digital abyss, never to be retrieved.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Database backups are essential lifelines for any data-driven operation. At their core, they involve the creation and storage of database copies at specific moments, acting as snapshots of data at those points in time. These snapshots become invaluable when faced with adversities like system crashes, unexpected data corruption, or even simple human errors leading to data deletions. Backups offer a &lt;em&gt;reliable&lt;/em&gt; mechanism to restore the database to its prior state, ensuring data remains intact and operations continue seamlessly.&lt;/p&gt;

&lt;p&gt;However, as straightforward as this may sound, backups come with their own set of challenges. There's the dread of silent failures where backups simply don't run as intended, with no significant alarms being raised. This can lead to startling gaps in backup history. Then there's the potential for data corruption. Imagine the horror of finding out that the backup, while seemingly successful, is corrupted and unusable. Further, not all backups capture the entirety of the database, sometimes leaving out chunks of data and causing partial restorations. A particularly tricky challenge arises post system upgrades, where older backups might not mesh well with the newer system versions. And when the clock's ticking during emergencies, long restoration times can add to the chaos, causing unwanted downtimes.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to prevent backup mishaps?
&lt;/h3&gt;

&lt;p&gt;Backups are very important but come with their own set of potential pitfalls. Addressing these proactively is usually the best course of action. You can look into the following techniques:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Review logs regularly&lt;/strong&gt;: You can check PostgreSQL logs and any error messages from your backup tool, such as &lt;code&gt;pg_dump&lt;/code&gt; or &lt;code&gt;pg_basebackup&lt;/code&gt;. The logs can provide vital clues to the root cause of the failure. Ensure you're using a compatible version of &lt;code&gt;pg_dump&lt;/code&gt; or &lt;code&gt;pg_basebackup&lt;/code&gt; with your PostgreSQL server. The logs can be found in the directory specified by the &lt;code&gt;log_directory&lt;/code&gt; configuration parameter (usually found in the &lt;code&gt;postgresql.conf&lt;/code&gt; file). The logs will contain entries for various events, and any errors related to backups will usually be evident with messages from tools like &lt;code&gt;pg_dump&lt;/code&gt; or &lt;code&gt;pg_basebackup&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Try daily backups or point-in-time recovery (PITR):&lt;/strong&gt; Daily backups can add a layer of protection against data loss. By taking consistent snapshots of the database, it's possible to recover data if mishaps occur, ensuring that the history and versioning of records is preserved. Similarly, point-in-time recovery (PITR) allows for database recovery to a specific moment, helping restore data to a precise moment in time and to its exact state at that time. This can be particularly useful when there's a need to revert unintended changes or in the event of data corruption.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stay vigilant:&lt;/strong&gt; Inspect error messages and logs from your backup tools to pinpoint issues and remember to keep your backup tools updated to avoid compatibility problems. Also, ensure there's adequate storage space for backups and double-check the specified paths to make sure backups are being saved to the correct and accessible locations.&lt;/li&gt;
&lt;/ul&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%2Fla09zmfin6wxkutci6e4.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%2Fla09zmfin6wxkutci6e4.gif" alt="Spooky" width="500" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Schema spectres: How misconfigured schemas can haunt you with data inconsistencies
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;On a fog-shrouded evening, deep within the echoing chambers of a startup, a lone developer embarked on what was to be a routine schema migration. He unwittingly unleashed an arcane script, and in so doing summoned tables twisted and contorted in unnatural ways, data that floated into the abyss, and records once familiar - appeared as gibberish incantations. Panic set in with the chilling knowledge that in the realm of databases, some mistakes can haunt persistently.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://xata.io/blog/postgres-schema-changes-pitas" rel="noopener noreferrer"&gt;Schema migrations&lt;/a&gt; can be intricate, especially when replication is involved.&lt;/p&gt;

&lt;p&gt;Start a migration, and there's always the risk it might stall. When that happens, the database can find itself in a twilight zone, neither in its previous state nor fully migrated. This in-between state can lead to unpredictable behaviors when applications interact with it. &lt;a href="https://xata.io/blog/pgroll-schema-migrations-postgres" rel="noopener noreferrer"&gt;Think you can just reverse a migration that went wrong? It's trickier than it sounds&lt;/a&gt;. Attempting to roll back a problematic change can sometimes introduce more inconsistencies than the original issue.&lt;/p&gt;

&lt;p&gt;The order of deploying these changes is of paramount importance. Some updates hinge on the successful implementation of others. Misordering them might not only disrupt data access but can also bring entire operations to a standstill. There's another layer of complexity when the development environment doesn't mirror the live production setup. Pushing a change meant for testing into the live environment can result in unexpected complications.&lt;/p&gt;

&lt;p&gt;Migrations not only change the structure of a database but can also modify the actual data. A seemingly simple task, like dividing a data column, can misrepresent or even lose data if done incorrectly.&lt;/p&gt;

&lt;p&gt;Now, factor in database replication. In systems with replicas, any schema change must be accurately mirrored across every single replica. Failure to ensure uniformity risks divergence between the primary database and its replicas, a situation that can lead to data integrity issues. Let’s break this down a bit further - The primary and the replica now have different data. The primary contains records in the new table that the replica doesn't have. This discrepancy is a major issue in replicated database setups, as they rely on consistent data across all instances for backup, load distribution, and failover scenarios.&lt;/p&gt;

&lt;p&gt;Misconfigured schema migrations and replication issues in databases, can lead to serious problems, such as data loss, data inconsistencies, and downtime. Addressing these challenges requires a combination of preventive, detective, and corrective measures.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to prevent schema migration mishaps?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Test before deploying:&lt;/strong&gt; Execute schema migrations in a development environment mirroring your production setup. It's important that the development environment closely resembles the production setup. Before implementing any schema migration, it's essential to test the potential performance implications on critical queries. This can be done using tools like the &lt;code&gt;EXPLAIN&lt;/code&gt; command in PostgreSQL to understand how queries will be executed post-migration, which can help identify potential bottlenecks or inefficiencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use schema version control:&lt;/strong&gt; When you make changes to your database schema, use tools that keep track of the versions and ensure that the changes are applied correctly. This allows you to easily manage and track the evolution of your schema over time. Additionally, &lt;a href="https://github.com/xataio/pgroll" rel="noopener noreferrer"&gt;pgroll&lt;/a&gt; provides features which further enhance the control and management of your data as well as schema migrations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;See no evil, just monitor:&lt;/strong&gt; Real-time monitoring of replication ensures you're alerted to any disruptions or lags. PostgreSQL’s &lt;code&gt;pg_stat_replication&lt;/code&gt; offers valuable insights and monitoring capabilities.&lt;/li&gt;
&lt;/ul&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%2F9yrkwgbnv92q72c6qxyh.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%2F9yrkwgbnv92q72c6qxyh.gif" alt="Vampire" width="472" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Vampiric VACUUM: Reclaiming dead tuples and space
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Amidst the quiet hum of over worked servers, the database began to groan, choked by the weight of dead tuples. A forgotten VACUUM process, now awakened, thirsted for resources, plunging the system into darkness. As data vanished, whispers of lost records haunted the corridors.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While "horror stories" about the VACUUM process in PostgreSQL can be dramatized for effect, there are some genuine concerns and potential mishaps that can arise if VACUUM isn't managed correctly.&lt;/p&gt;

&lt;p&gt;In PostgreSQL, VACUUM is a maintenance operation that helps reclaim storage space and maintain the health of the database. Over time, as data gets updated or deleted, it leaves behind what are known as "dead tuples." These are essentially old versions of rows that are no longer needed. If left unchecked, these dead tuples can accumulate and lead to database bloat and degraded performance. VACUUM can reclaim storage and collect statistics on performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There are different types of VACUUM operations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Regular VACUUM:&lt;/strong&gt; This reclaims space but doesn't return it to the operating system. Instead, it makes the space available for reuse by the database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VACUUM FULL:&lt;/strong&gt; This not only reclaims space but also compacts tables and returns the freed space to the operating system. It's a more intensive operation and can take longer, locking the tables in the process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Autovacuum:&lt;/strong&gt; To avoid manual vacuuming, PostgreSQL has an &lt;em&gt;autovacuum&lt;/em&gt; process that runs automatically in the background. It checks tables for dead tuples and vacuums them periodically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;VACUUM is a critical maintenance task, but it must be configured and executed correctly for optimal database performance and to prevent problems. Failing to run VACUUM regularly can result in excessive disk usage because the process reclaims space from deleted or outdated tuples. If this task is overlooked, the database can grow in size, consuming more disk space than necessary, and in severe cases, it might exhaust all available space.&lt;/p&gt;

&lt;p&gt;In PostgreSQL, transaction IDs are finite. Without VACUUM cleaning up old IDs, the system can experience a "wraparound." This means older data entries could become inaccessible, posing a risk of data loss. By regularly running VACUUM, you can prevent the system from reaching the point where a wraparound would cause problems, and safeguard the accessibility and integrity of your data.&lt;/p&gt;

&lt;p&gt;However, sometimes &lt;em&gt;vacuuming&lt;/em&gt; can be too much! When a VACUUM FULL is executed during high database activity, it can cause significant slowdowns. A VACUUM FULL operation requires exclusive access to the table it's processing. This means other operations on that table are paused, which can create a bottleneck, especially if the table is frequently accessed. It's important to time these operations outside of peak periods to avoid disrupting the database's performance.&lt;/p&gt;

&lt;p&gt;Last but not least, the autovacuum process, if not configured correctly, can either be too aggressive, affecting performance, or too lax, allowing dead tuples to accumulate. Turning off autovacuum on specific tables with the intention of manual oversight can be risky. If forgotten, it could lead to unchecked table growth.&lt;/p&gt;

&lt;p&gt;If a VACUUM operation is interrupted, especially a manual one, it might leave behind temporary files that occupy disk space.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to prevent VACUUM mishaps?
&lt;/h3&gt;

&lt;p&gt;The VACUUM process in PostgreSQL is important for maintaining database health, but managing it effectively requires understanding its nuances. Here are some solutions and best practices to address common VACUUM-related challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tune autovacuum:&lt;/strong&gt; You can adjust the &lt;code&gt;autovacuum_naptime&lt;/code&gt; parameter to control how often the autovacuum process checks tables for cleanup. Modify or tweak the &lt;code&gt;autovacuum_vacuum_scale_factor&lt;/code&gt; and &lt;code&gt;autovacuum_analyze_scale_factor&lt;/code&gt; to determine when a table should be vacuumed or analyzed based on the proportion of changed tuples. Additionally, you can use &lt;code&gt;autovacuum_vacuum_cost_limit&lt;/code&gt; and &lt;code&gt;autovacuum_vacuum_cost_delay&lt;/code&gt; to influence how aggressive the autovacuum process is, preventing it from consuming too many resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Try manual VACUUM:&lt;/strong&gt; Run a manual &lt;code&gt;VACUUM&lt;/code&gt; if you know a table has accumulated a significant number of dead tuples. Use &lt;code&gt;VACUUM (VERBOSE)&lt;/code&gt; to get detailed information about the vacuuming process, helping diagnose potential issues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use less invasive tools:&lt;/strong&gt; Instead of frequently using &lt;code&gt;VACUUM FULL&lt;/code&gt;, which is resource-intensive and locks tables, consider using the less invasive &lt;code&gt;pg_repack&lt;/code&gt; extension to reclaim space without the extensive locks. Monitor disk space usage, table bloat, and autovacuum activity, and set up alerts for scenarios like nearing transaction ID wraparound or excessive table bloat.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Increase maintenance work memory:&lt;/strong&gt; In PostgreSQL, the &lt;code&gt;maintenance_work_mem&lt;/code&gt; configuration parameter determines how much memory can be allocated for maintenance operations. Boosting &lt;code&gt;maintenance_work_mem&lt;/code&gt; can help speed up the VACUUM process by allowing it to sort and process more data in memory.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Trick or treat
&lt;/h2&gt;

&lt;p&gt;Want to share some of your own database horror stories? Reach out to us on &lt;a href="https://xata.io/discord" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; or follow us on &lt;a href="https://twitter.com/xata" rel="noopener noreferrer"&gt;X | Twitter&lt;/a&gt;. We'd love to hear your thoughts, answer your questions, and keep you updated on the latest at Xata.&lt;/p&gt;

</description>
      <category>database</category>
      <category>postgressql</category>
      <category>postgres</category>
      <category>developer</category>
    </item>
    <item>
      <title>Xata Community Spotlight: Extracting News and Research With Chat Interactions</title>
      <dc:creator>Joan</dc:creator>
      <pubDate>Fri, 20 Oct 2023 14:33:32 +0000</pubDate>
      <link>https://dev.to/xata/xata-community-spotlight-extract-news-and-research-with-chat-interactions-55fg</link>
      <guid>https://dev.to/xata/xata-community-spotlight-extract-news-and-research-with-chat-interactions-55fg</guid>
      <description>&lt;p&gt;Today we’re putting the community spotlight on two developers based out of Paraguay, &lt;a href="https://github.com/gomezag" rel="noopener noreferrer"&gt;Agustín Gomez&lt;/a&gt; and &lt;a href="https://github.com/quanturtle" rel="noopener noreferrer"&gt;Alvaro Machuca&lt;/a&gt;. They discovered Xata while looking for a serverless database offering that supported vector embeddings. Since starting with Xata a few months ago, Agustín and Alvaro have built real world applications for conversational search with data; they provide chat experiences on very specific sets of data. Both apps have a similar tech stack and build experiences on top of Python / Django, Xata, and Vercel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generative search solutions
&lt;/h2&gt;

&lt;p&gt;The first application is called &lt;a href="https://www.chatgov.com.py/" rel="noopener noreferrer"&gt;ChatGOV&lt;/a&gt; and provides a simple and interactive way to converse about Paraguayan law.&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%2Fj2y2vdjuflnnawucjl58.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%2Fj2y2vdjuflnnawucjl58.png" alt="ChatGOV beta" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All publicly accessible Paraguayan law was scraped and integrated into a Xata database. With these articles, &lt;a href="https://xata.io/docs/integrations/langchain" rel="noopener noreferrer"&gt;LangChain&lt;/a&gt; and OpenAI were used to create embeddings, which were then stored in the same record as the respective legal documents. The ease of having the embeddings stored directly alongside the relational data made the journey to production extremely fast.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;"Having a way to store all the vectors along with your records saves me time and I don’t have to go fight with another client like Pinecone. The fact that it’s in one stop shop for everything is great! I want to build products, not mount database services."&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Alvaro Machuca - Co-Founder of ChatGOV&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;/blockquote&gt;




&lt;p&gt;The second application is called &lt;a href="https://briefly-news.vercel.app/" rel="noopener noreferrer"&gt;Briefly News&lt;/a&gt; (&lt;a href="https://github.com/gomezag/briefly-news" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;). It provides a quick way to navigate the Paraguayan news and delve deeper into details about individuals mentioned in the articles.&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%2Fu15ztbiu2n83e12l9ez1.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%2Fu15ztbiu2n83e12l9ez1.png" alt="Briefly News" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This project is still early days, but has already scraped and ingested nearly 70,000 articles. It provides end-to-end workflows for filtering news articles based on certain criteria, visualizing common terms with a word cloud, aggregating statistics of the news articles, and a chat-based workflow for learning more about the people featured in a news article.&lt;/p&gt;

&lt;p&gt;Rather than having disparate services and data stores to handle each one of these use cases, all of the data is simply stored in a Xata database. The &lt;a href="https://xata.io/docs/sdk/python/overview" rel="noopener noreferrer"&gt;Xata Python SDK&lt;/a&gt; and ORM-like experience was used for &lt;a href="https://xata.io/docs/sdk/filtering" rel="noopener noreferrer"&gt;filtering&lt;/a&gt;, &lt;a href="https://xata.io/docs/sdk/search" rel="noopener noreferrer"&gt;full-text search&lt;/a&gt;, &lt;a href="https://xata.io/docs/sdk/aggregate" rel="noopener noreferrer"&gt;aggregations&lt;/a&gt; and &lt;a href="https://xata.io/docs/sdk/ask" rel="noopener noreferrer"&gt;chat features&lt;/a&gt;.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;"All in all, I have to say, I was really surprised at how easy it was to adopt Xata and just jump in and start using it. I haven’t had to look at my database in 3 months, it’s stable, no maintenance required and it just works."&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Agustín Gomez - Software Engineer by Day, Superhero by Night&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Feedback and feature requests
&lt;/h2&gt;

&lt;p&gt;Having been using Xata since early this year, we asked both Agustín and Alvaro what their favorite aspects of Xata have been so far, and what they’d like to see on our roadmap. Here were some of the reasons Agustín and Alvaro chose Xata for their chat solutions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Transition from prototype to production.&lt;/strong&gt; For both projects it was extremely easy to prototype, iterate quickly and turn on for production use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in vector DB.&lt;/strong&gt; Not having to worry about another database service specialized for vector embeddings was a huge benefit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python SDK.&lt;/strong&gt; The Python SDK has steadily seen improvements with each release; it’s been great to see the progression over time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When asked what they’re looking forward to seeing, here’s what they shared.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Usage observability.&lt;/strong&gt; As their project grows, they’d like to see more details about their usage. Luckily, this is already on &lt;a href="https://xata.io/roadmap" rel="noopener noreferrer"&gt;our roadmap&lt;/a&gt; and in the works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python functions.&lt;/strong&gt; It would be beneficial to have additional helper functions in the Python SDK. A Django-like &lt;a href="https://docs.djangoproject.com/en/4.2/ref/models/querysets/#get-or-create" rel="noopener noreferrer"&gt;&lt;code&gt;get_or_create&lt;/code&gt;&lt;/a&gt; function would be helpful for the web scraping use case. Sometimes an article needs to be created or updated. Having better warnings for pagination to ensure all data is returned also would have also been nice to see from the client.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in embedding generation.&lt;/strong&gt; We discussed a bit about some ✨. Simplifying the embedding creation process with more dynamic columns and supporting lighter weight embeddings would be great for their use case.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re interested in learning more about how to build practical solutions for generative search and the types of technical challenges you may find along the way, you can find Agustín, Alvaro, and the Xata team on our &lt;a href="https://xata.io/discord" rel="noopener noreferrer"&gt;Discord server&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Share your story
&lt;/h2&gt;

&lt;p&gt;Do you have a similar story or community contribution you’d like to share? &lt;a href="//mailto:communityspotlight@xata.io"&gt;Send us an email&lt;/a&gt; if you’d like to be featured in our community spotlight.&lt;br&gt;
Until then, happy building 🦋&lt;/p&gt;

</description>
      <category>community</category>
      <category>research</category>
      <category>developers</category>
      <category>vectordatabase</category>
    </item>
    <item>
      <title>Xata's JSON Column Type</title>
      <dc:creator>Joan</dc:creator>
      <pubDate>Tue, 17 Oct 2023 17:33:51 +0000</pubDate>
      <link>https://dev.to/xata/xatas-json-column-type-29ab</link>
      <guid>https://dev.to/xata/xatas-json-column-type-29ab</guid>
      <description>&lt;p&gt;Data models that use schemas are great! At Xata, we believe they're a solid choice in most scenarios. But, we also know that not every piece of data fits perfectly into the relational model or is not as convenient as schemaless, and sometimes, especially in the early stages of a project, you want something more flexible and straightforward. That's where using JSON documents within a relational data store comes in handy. It offers the best of both worlds – structure when you need it and a bit of freedom when you don't.&lt;/p&gt;

&lt;p&gt;Many of our users have been &lt;a href="https://xata.canny.io/feature-requests/p/json-objects" rel="noopener noreferrer"&gt;asking for this feature&lt;/a&gt;, and as part of &lt;a href="https://dev.to/blog/launch-week-august-2023"&gt;launch week&lt;/a&gt; we are happy to announce that it's finally here.&lt;/p&gt;

&lt;p&gt;Basic support for &lt;a href="https://xata.io/docs/sdk/filtering#json" rel="noopener noreferrer"&gt;JSON column type&lt;/a&gt; has been added and it will bring many benefits including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced flexibility&lt;/strong&gt; by providing a way to store schemaless data in a relational database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data integrity&lt;/strong&gt; with JSON validation according to &lt;a href="https://www.rfc-editor.org/rfc/rfc7159.html" rel="noopener noreferrer"&gt;RFC 7159&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streamlined development&lt;/strong&gt; that stores any unstructured data directly with no need to handle a schema or data conversions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Efficient queries&lt;/strong&gt; so you can apply many filters to nested nodes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search functionality&lt;/strong&gt;, as JSON documents are indexed as any other Xata data type and can be searched using the &lt;a href="https://xata.io/docs/sdk/search" rel="noopener noreferrer"&gt;full-text search&lt;/a&gt; capabilities of Xata&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We plan on extending Xata support for JSON even further in the future, but the current capabilities are already very powerful and will solve most use cases.&lt;br&gt;
Below we provide examples of how you can start using JSON documents in Xata today.&lt;/p&gt;

&lt;p&gt;Let's think a bit about a simple data model for an online shop. Suppose we have a &lt;em&gt;Products&lt;/em&gt; table.&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%2Fix0wnx8wyb2vgoic6po6.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%2Fix0wnx8wyb2vgoic6po6.png" alt="Products table" width="245" height="324"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Creating a JSON column
&lt;/h2&gt;

&lt;p&gt;Sometimes different categories of products have completely different specs, so we don't want to create a column for each of them.&lt;/p&gt;

&lt;p&gt;We can use a JSON column to store the product details. Let's do this by adding a &lt;code&gt;details&lt;/code&gt; field to our table. You can do this via the UI or via the API like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json-doc"&gt;&lt;code&gt;&lt;span class="c1"&gt;// POST https://{workspace}.{region}.xata.sh/db/{db}:{branch}/tables/{table}/columns&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"details"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"json"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's add a few products with different details, for instance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A t-shirt with size and color&lt;/li&gt;
&lt;li&gt;A book with author, ISBN and number of pages&lt;/li&gt;
&lt;li&gt;A climbing rope with a length and a thickness&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;
  
  
  TypeScript
&lt;/h4&gt;


&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const record1 = await xata.db.Products.create({
  name: 'Xata xwag T-shirt',
  details: {
    color: 'purple',
    size: 'M',
  }
});
const record2 = await xata.db.Products.create({
  name: 'Meditations',
  details: {
    author: 'Marcus Aurelius',
    isbn: '978-0140449334',
    pages: 304
  }
});
const record3 = await xata.db.Products.create({
  name: 'Long climbing rope',
  details: {
    length: 80,
    thickness: 9.8,
    color: 'blue',
  }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;
  
  
  Python
&lt;/h4&gt;


&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;record1 = xata.records().insert("Products", {
  "name": "Xata xwag T-shirt",
  "details": {
    "color": "purple",
    "size": "M",
  }
})
record2 = xata.records().insert("Products", {
  "name": "Meditations",
  "details": {
    "author": "Marcus Aurelius",
    "isbn": "978-0140449334",
    "pages": 304
  }
})
record3 = xata.records().insert("Products", {
  "name": "Long climbing rope",
  "details": {
    "length": 80,
    "thickness": 9.8,
    "color": "blue"
  }
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;
  
  
  JSON
&lt;/h4&gt;


&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json-doc"&gt;&lt;code&gt;&lt;span class="c1"&gt;// POST https://{workspace}.{region}.xata.sh/db/{db}:{branch}/tables/{table}/data&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Xata xwag T-shirt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;color&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;purple&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;size&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;M&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Meditations"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;author&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Marcus Aurelius&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;isbn&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;978-0140449334&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;pages&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 304}"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Long climbing rope"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;length&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 80, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;thickness&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 9.8, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;color&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;blue&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's important to note that the JSON documents are processed and stored in a binary format in order to improve querying and storage performance.&lt;br&gt;
This has the following implications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;White spaces are not preserved&lt;/li&gt;
&lt;li&gt;Key order is not preserved&lt;/li&gt;
&lt;li&gt;In case of duplicate key, only the last one is stored&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Querying JSON documents
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The arrow notation &lt;code&gt;-&amp;gt;&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This is PostgreSQL's syntax for navigating JSON fields. It's used to access the value of any JSON node, no matter how deep in the tree.&lt;/p&gt;

&lt;p&gt;Xata uses a similar notation to query data and apply some of the existing filters to any JSON value. PostgreSQL uses different operators and casting&lt;br&gt;
depending on the data types, but Xata is able to infer the data type from the provided value and apply the correct operator.&lt;/p&gt;

&lt;p&gt;So far, comparison by strings and numbers is supported but this will be extended in the near future.&lt;/p&gt;




&lt;h3&gt;
  
  
  Filter products size 'M'
&lt;/h3&gt;




&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;
  
  
  TypeScript
&lt;/h4&gt;


&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const records = await xata.db.Products.filter({
  "details-&amp;gt;size": 'M'
}).getMany();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;
  
  
  Python
&lt;/h4&gt;


&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;records = xata.data().query("Products", {
  "filter": {
    "details-&amp;gt;size": "M"
  }
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;
  
  
  SQL
&lt;/h4&gt;


&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// POST https://{workspace}.{region}.xata.sh/db/{db}:{branch}/sql

{
  "statement": "SELECT * FROM \"Products\" WHERE details-&amp;gt;&amp;gt;'size' = 'M';"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;
  
  
  JSON
&lt;/h4&gt;


&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// POST https://{workspace}.{region}.xata.sh/db/{db}:{branch}/tables/{table}/query

{
  "filter": {
    "details-&amp;gt;size": "M"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Filter products with a length greater than 50 meters
&lt;/h3&gt;




&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;
  
  
  TypeScript
&lt;/h4&gt;


&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const records = await xata.db.Products.filter({
  "details-&amp;gt;length": {
    "$gt": 50
  }
}).getMany();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;
  
  
  Python
&lt;/h4&gt;


&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;records = xata.data().query("Products", {
  "filter": {
    "details-&amp;gt;length": {
      "$gt": 50
    }
  }
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;
  
  
  SQL
&lt;/h4&gt;


&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// POST https://{workspace}.{region}.xata.sh/db/{db}:{branch}/sql

{
  "statement": "SELECT * FROM \"Products\" WHERE (details-&amp;gt;&amp;gt;length)::numeric &amp;gt; 50;"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;
  
  
  JSON
&lt;/h4&gt;


&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// POST https://{workspace}.{region}.xata.sh/db/{db}:{branch}/tables/{table}/query

{
  "filter": {
    "details-&amp;gt;length": {
      "$gt": 50
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Check for a substring in a nested JSON node
&lt;/h3&gt;




&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;
  
  
  TypeScript
&lt;/h4&gt;


&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const records = await xata.db.Products.filter({
  "details-&amp;gt;author": {
    "$contains": "Marcus"
  }
}).getMany();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;
  
  
  Python
&lt;/h4&gt;


&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;records = xata.data().query("Products", {
  "filter": {
    "details-&amp;gt;author": {
      "contains": "Marcus"
    }
  }
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;
  
  
  SQL
&lt;/h4&gt;


&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// POST https://{workspace}.{region}.xata.sh/db/{db}:{branch}/sql
{
  "statement": "SELECT * FROM \"Products\" WHERE details-&amp;gt;&amp;gt;author LIKE '%Marcus%';"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;
  
  
  JSON
&lt;/h4&gt;


&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// POST https://{workspace}.{region}.xata.sh/db/{db}:{branch}/tables/{table}/query
{
  "filter": {
    "details-&amp;gt;author": {
      "$contains": "Marcus"
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Other general control or negation operators work as well
&lt;/h3&gt;




&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;
  
  
  TypeScript
&lt;/h4&gt;


&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;xata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$not&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;details-&amp;gt;length&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$gt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;getMany&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;
  
  
  Python
&lt;/h4&gt;


&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;records = xata.data().query("Products", {
    "filter": {
        "$not": {
            "details-&amp;gt;length": {
                "$gt": 50
            }
        }
    }
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;
  
  
  SQL
&lt;/h4&gt;


&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// POST https://{workspace}.{region}.xata.sh/db/{db}:{branch}/sql
{
  "statement": "SELECT * FROM \"Products\" WHERE NOT (details-&amp;gt;&amp;gt;length)::numeric &amp;gt; 50;"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;
  
  
  JSON
&lt;/h4&gt;


&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// POST https://{workspace}.{region}.xata.sh/db/{db}:{branch}/tables/{table}/query
{
  "filter": {
    "$not": {
      "details-&amp;gt;length": {
        "$gt": 50
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;p&gt;Xata is committed to simplifying the way you work with data. We will keep improving our offering by both adding more rich data types and extending the capabilities of the current ones.&lt;br&gt;
Basic JSON support is one more step in that direction along with the previously released &lt;a href="https://xata.io/blog/file-attachments" rel="noopener noreferrer"&gt;files attachments&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have feedback or questions, you can reach out to us on &lt;a href="https://xata.io/discord" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; or &lt;a href="https://twitter.com/xata" rel="noopener noreferrer"&gt;X / Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>schema</category>
      <category>database</category>
      <category>json</category>
      <category>sql</category>
    </item>
    <item>
      <title>Announcing the Winners of the Xata Content Hackathon</title>
      <dc:creator>Joan</dc:creator>
      <pubDate>Tue, 17 Oct 2023 13:37:28 +0000</pubDate>
      <link>https://dev.to/xata/announcing-the-winners-of-the-xata-content-hackathon-12m3</link>
      <guid>https://dev.to/xata/announcing-the-winners-of-the-xata-content-hackathon-12m3</guid>
      <description>&lt;p&gt;To commemorate our most recent &lt;a href="https://xata.io/blog/launch-week-august-2023" rel="noopener noreferrer"&gt;launch week&lt;/a&gt;, we called upon our vibrant community to showcase their creativity, enthusiasm, and interest for Xata. The challenge? Write a captivating blog post, create an engaging video, or simply spread the word about Xata. And you all delivered in style!&lt;/p&gt;

&lt;p&gt;The anticipation has been building, and now, we're thrilled to reveal the winners of our content hackathon.&lt;/p&gt;

&lt;p&gt;🥁 Drum roll, please! 🥁&lt;/p&gt;

&lt;p&gt;Without further ado, the winners of our hackathon (in no particular order) are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/Aboo_Turaab" rel="noopener noreferrer"&gt;@Aboo_Turaab&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/dotAadarsh" rel="noopener noreferrer"&gt;@dotAadarsh&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/terieyenike" rel="noopener noreferrer"&gt;@terieyenike&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/moe_rayo" rel="noopener noreferrer"&gt;@moe_rayo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/ishnbedi" rel="noopener noreferrer"&gt;@ishnbedi&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🦋 Congratulations to our winners for your exceptional contributions! We'll be in touch to award the $500 cash prizes to each of you.&lt;/p&gt;

&lt;p&gt;To every participant, a heartfelt thank you and kudos! Your dedication, creativity, and efforts have truly amazed us. Each contribution has been a testament to the incredible talent and passion within our community.&lt;/p&gt;

&lt;p&gt;Curious about some of the projects our community created? Take a look at the content below to see how users incorporated Xata into their winning development projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Winning Content
&lt;/h2&gt;

&lt;p&gt;From data retrieval and building libraries to crafting developer portfolios and wishlist apps, discover how our community harnessed Xata to simplify their data tasks.&lt;/p&gt;




&lt;h3&gt;
  
  
  Build an Online Library
&lt;/h3&gt;

&lt;p&gt;Join @Aboo_Turaab in this tutorial, &lt;strong&gt;Building an online library using Xata&lt;/strong&gt;, and learn how to use Xata's file attachment capabilities.&lt;/p&gt;

&lt;p&gt;In the related blog post &lt;strong&gt;File attachment in Xata Database: How to build an Online Library&lt;/strong&gt;, @Aboo_Turaab discusses Xata's file attachment feature and uses it to build an online library without relying on external storage services.&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%2Fi8nmyh9pbczw4uer2puk.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%2Fi8nmyh9pbczw4uer2puk.png" alt=" Build an online library post" width="800" height="932"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Optimize Data Retrieval
&lt;/h3&gt;

&lt;p&gt;In &lt;strong&gt;Optimizing data retrieval&lt;/strong&gt;, @dotAadarsh addresses Xata's innovative approach to optimizing database queries and looks into the pesky N+1 problem in one-to-many relationships.&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%2F4jzwwbvplrpek115ozoq.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%2F4jzwwbvplrpek115ozoq.png" alt="Optimize data retrieval post" width="800" height="715"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Build an Efficient Waitlist App
&lt;/h3&gt;

&lt;p&gt;Through &lt;strong&gt;Building an efficient waitlist app with Next.js and Xata&lt;/strong&gt;, &lt;a class="mentioned-user" href="https://dev.to/terieyenike"&gt;@terieyenike&lt;/a&gt; introduces a waitlist application suited for impactful pre-launch marketing campaigns.&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%2Fo4ukj5yz9nqzvx4310gp.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%2Fo4ukj5yz9nqzvx4310gp.png" alt="Build an efficient waitlist app post" width="800" height="843"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Build a File Explorer
&lt;/h3&gt;

&lt;p&gt;Follow along with @moe_rayo in &lt;strong&gt;How to build a file explorer using Xata and Vue.js&lt;/strong&gt; as he demonstrates the process of creating a user-friendly file explorer.&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%2Fs48fhwv0vxw2b383mka1.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%2Fs48fhwv0vxw2b383mka1.png" alt="Build a file explorer post" width="800" height="775"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Create a Developer Portfolio
&lt;/h3&gt;

&lt;p&gt;In &lt;strong&gt;Creating an amazing developer portfolio using the NeXuS stack&lt;/strong&gt;, @ishnbedi uses Xata to craft a standout developer portfolio that resonates with both recruiters and clients alike.&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%2F71cm3wivhx1e6r5vo79r.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%2F71cm3wivhx1e6r5vo79r.png" alt="Create a developer portfolio post" width="800" height="589"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  You're Soaring! Don't Stop Now
&lt;/h2&gt;

&lt;p&gt;Again, a roaring applause for all participants 🎉 Expect to receive some gifted exclusive Xata swag soon.&lt;/p&gt;

&lt;p&gt;The fun doesn't have to end here, though. Participate in &lt;a href="https://hacktoberfest.com/" rel="noopener noreferrer"&gt;Hacktoberfest 2023&lt;/a&gt; and contribute to Xata's open-source repos like &lt;a href="https://github.com/xataio/pgroll" rel="noopener noreferrer"&gt;pg-roll&lt;/a&gt;, our &lt;a href="https://github.com/xataio/xata-py" rel="noopener noreferrer"&gt;Python SDK&lt;/a&gt;, or &lt;a href="https://github.com/xataio/mdx-docs" rel="noopener noreferrer"&gt;docs&lt;/a&gt;. Also, don't forget to keep a lookout for our next exciting challenge.&lt;/p&gt;

&lt;p&gt;To learn more about Xata, &lt;a href="https://app.xata.io/signin" rel="noopener noreferrer"&gt;sign in&lt;/a&gt; and experiment, check out our &lt;a href="https://xata.io/docs" rel="noopener noreferrer"&gt;docs&lt;/a&gt;, or join the conversation on &lt;a href="https://xata.io/discord" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; and &lt;a href="https://twitter.com/xata" rel="noopener noreferrer"&gt;X / Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At Xata, we are aflutter with pride and gratitude for every single participant. Your inventive efforts underline what we've always believed — our community is our strongest asset. Your dedication, zest, and ingenuity reaffirm our mission. 🦋&lt;/p&gt;

&lt;p&gt;Here's to more creativity, coding, and community camaraderie! Keep making waves, and remember, we're here cheering you on.&lt;/p&gt;

&lt;p&gt;Until our next adventure together... 🚀&lt;/p&gt;

</description>
      <category>product</category>
      <category>database</category>
      <category>tutorial</category>
      <category>hackathon</category>
    </item>
    <item>
      <title>Build a Chatbot With OpenAI, Vercel AI and Xata</title>
      <dc:creator>Joan</dc:creator>
      <pubDate>Fri, 13 Oct 2023 16:33:43 +0000</pubDate>
      <link>https://dev.to/xata/build-a-chatbot-with-openai-vercel-ai-and-xata-2cn6</link>
      <guid>https://dev.to/xata/build-a-chatbot-with-openai-vercel-ai-and-xata-2cn6</guid>
      <description>&lt;p&gt;In today's data-driven world, efficient interaction with databases is a crucial aspect of many applications. But what if we could go beyond conventional search methods and enable a natural language conversation with our databases?&lt;/p&gt;

&lt;p&gt;At Xata, we aim to provide developers with the tools to build powerful applications that can interact with data in a natural way. Built-in with our core APIs and SDKs, we offer a powerful &lt;a href="https://xata.io/docs/sdk/ask" rel="noopener noreferrer"&gt;ask endpoint&lt;/a&gt; that allows you to ask questions about your data and get the answers that matter most.&lt;/p&gt;

&lt;p&gt;However, how would you integrate Xata with an existing application built with OpenAI? In this tutorial, we'll show you how to integrate OpenAI's &lt;a href="https://platform.openai.com/docs/guides/gpt/function-calling" rel="noopener noreferrer"&gt;function calling&lt;/a&gt; feature with Xata's TypeScript SDK to create a chatbot that can use search to answer questions about your data.&lt;/p&gt;

&lt;p&gt;In a &lt;a href="https://xata.io/blog/chatgpt-on-your-data" rel="noopener noreferrer"&gt;previous post&lt;/a&gt;, we introduced the concept of using our built-in &lt;a href="https://xata.io/docs/sdk/ask" rel="noopener noreferrer"&gt;ask endpoint&lt;/a&gt; to simplify the process of querying your data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we begin, make sure you have the following prerequisites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Existing project configured with the &lt;a href="https://xata.io/docs/getting-started/installation" rel="noopener noreferrer"&gt;Xata CLI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://app.xata.io/settings" rel="noopener noreferrer"&gt;Xata API key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://platform.openai.com/account/api-keys" rel="noopener noreferrer"&gt;OpenAI API key&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In your preferred serverless environment, make sure you install the &lt;a href="https://github.com/openai/openai-node" rel="noopener noreferrer"&gt;OpenAI API Library&lt;/a&gt; and &lt;a href="https://github.com/vercel-labs/ai" rel="noopener noreferrer"&gt;Vercel AI library&lt;/a&gt; to get started.&lt;/p&gt;

&lt;p&gt;After ensuring your prerequisites are met, you can integrate Xata with your existing OpenAI application in three steps: Define a search function for AI, ask questions about your data, and run completions while streaming the results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 - Defining a search function
&lt;/h2&gt;

&lt;p&gt;First, we'll define a function that allows us to search our database using OpenAI's &lt;a href="https://platform.openai.com/docs/guides/gpt/function-calling" rel="noopener noreferrer"&gt;function calling&lt;/a&gt; feature.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CompletionCreateParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;full_text_search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Full text search on a branch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;The search query&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;query&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we want to allow OpenAI to fine-tune the search results, we can add more options to the &lt;code&gt;parameters&lt;/code&gt; object. For example, we can add a &lt;code&gt;fuzziness&lt;/code&gt; parameter to allow for fuzzy search.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CompletionCreateParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;full_text_search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Full text search on a branch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;The search query&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;fuzziness&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Maximum levenshtein distance for fuzzy search, minimum 0, maximum 2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;query&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2 - Ask a question about your data
&lt;/h2&gt;

&lt;p&gt;Now that we have our search function defined, we can use the &lt;a href="https://www.npmjs.com/package/openai" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt; library to ask a question about our data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;openai&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="c1"&gt;// Make sure to properly load and set your OpenAI API key here&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OPENAI_API_KEY&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gpt-3.5-turbo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;question&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nx"&gt;functions&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the functions defined, the AI will be able to call the &lt;code&gt;full_text_search&lt;/code&gt; function and pass the &lt;code&gt;query&lt;/code&gt; parameter with the parts of the question that are relevant to the search.&lt;/p&gt;

&lt;p&gt;To enhance the results, we can include additional information in the &lt;code&gt;messages&lt;/code&gt; array as system messages. For instance, we can provide instructions to the AI or offer hints related to our database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;branches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBranchDetails&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;branch&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;question&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
        Workspace: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
        Region: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
        Database: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
        Branch: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
        Schema: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;

        Reply to the user about the data in the database, do not reply about other topics.
        Only use the functions you have been provided with, and use them in the way they are documented.
      `&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nx"&gt;functions&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OpenAI provides a variety of models, which you can find listed &lt;a href="https://platform.openai.com/docs/models/overview" rel="noopener noreferrer"&gt;here&lt;/a&gt;. If you require a different model that aligns more closely with your specific use case, you can easily switch the &lt;code&gt;model&lt;/code&gt; parameter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 - Running the completion and streaming the results
&lt;/h2&gt;

&lt;p&gt;Finally, we can run the completion and stream the results to the client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAIStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;experimental_onFunctionCall&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;createFunctionCallMessages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;full_text_search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchAndFilter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;searchBranch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;fuzziness&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fuzziness&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newMessages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createFunctionCallMessages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;newMessages&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;functions&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unknown OpenAI function call name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StreamingTextResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have chosen to stream the results to the client, but you can also wait for the completion to finish and return the results as a single response.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bonus - Building an interactive chatbot UI
&lt;/h3&gt;

&lt;p&gt;With React and the &lt;a href="https://sdk.vercel.ai/docs/api-reference/use-chat" rel="noopener noreferrer"&gt;&lt;code&gt;useChat&lt;/code&gt; hook&lt;/a&gt; you can easily create a chatbot that can answer questions about your data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useChat&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="c1"&gt;// The route to the endpoint we have just created&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/chat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;initialMessages&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Congratulations! You've just built a powerful system that combines the capabilities of OpenAI, Vercel AI and Xata's database API. Users can now engage in natural language conversations with their databases, and OpenAI will utilize the provided functions to perform searches and retrieve relevant information.&lt;/p&gt;

&lt;p&gt;By following this tutorial, you've learned how to integrate multiple APIs, handle requests and create interactive responses. This foundation can be extended to create even more sophisticated systems that enable seamless human-machine interactions with data.&lt;/p&gt;

&lt;p&gt;If you want to learn more about Xata's database API, check out our &lt;a href="https://xata.io/docs" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; and come say hi on our &lt;a href="https://xata.io/discord" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; if you have any questions.&lt;/p&gt;

&lt;p&gt;Happy coding! 🦋&lt;/p&gt;

</description>
      <category>openai</category>
      <category>vercel</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The Xata Playground Now Runs Python in the Browser</title>
      <dc:creator>Joan</dc:creator>
      <pubDate>Fri, 13 Oct 2023 16:08:05 +0000</pubDate>
      <link>https://dev.to/xata/the-xata-playground-now-runs-python-in-the-browser-49pb</link>
      <guid>https://dev.to/xata/the-xata-playground-now-runs-python-in-the-browser-49pb</guid>
      <description>&lt;p&gt;At Xata, we're committed to providing you with a versatile and powerful UI to interact with your data. That's why early on we built the Xata Playground, a web-based IDE that allows you to write code in TypeScript and SQL to query your data in Xata.&lt;/p&gt;

&lt;p&gt;Now, the Xata Playground includes Python! You can write code in Xata's Playground using Python, TypeScript, and SQL.&lt;/p&gt;

&lt;p&gt;In this post, we'll do a technical deep dive into how the Xata Playground works and how we added Python to the mix.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Xata Playground
&lt;/h2&gt;

&lt;p&gt;As a new hire that had just marked my first month at Xata, I opened a pull request with the first version of the Xata Playground. It was a very simple proof of concept that I wanted to show during our weekly team meeting.&lt;/p&gt;

&lt;p&gt;The pull request introduced a &lt;a href="https://microsoft.github.io/monaco-editor/" rel="noopener noreferrer"&gt;monaco-editor&lt;/a&gt; where you could write TypeScript code and run it to see the results in a separate panel. The main goal was to allow users to experiment and try out our TypeScript SDK without having to install anything.&lt;/p&gt;

&lt;p&gt;Both internally and with some early adopters, we noticed that it was something we had to invest in. We decided to iterate quickly over the proof of concept and release it as soon as possible. Just a month later, we launched the first version of the Xata Playground, and it has remained one of our most popular features ever since.&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%2Fjyxxmr9zwjz2tbxntzl1.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%2Fjyxxmr9zwjz2tbxntzl1.png" alt="Xata Playground proof of concept" width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;The Playground was built following the inspiration of other online IDEs like &lt;a href="https://www.typescriptlang.org/play" rel="noopener noreferrer"&gt;TypeScript Playground&lt;/a&gt;, &lt;a href="https://codesandbox.io/" rel="noopener noreferrer"&gt;CodeSandBox&lt;/a&gt;, or &lt;a href="https://stackblitz.com/" rel="noopener noreferrer"&gt;StackBlitz&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One of the core principles, was that all code should be executed in the browser. We wanted to avoid the complexity of having a backend service that would execute the code and return the results. This would have required work to secure the code execution, and it would have added extra latency to the execution.&lt;/p&gt;

&lt;p&gt;To achieve this, we use &lt;a href="https://rollupjs.org" rel="noopener noreferrer"&gt;Rollup&lt;/a&gt; to bundle the code and a &lt;a href="https://github.com/SferaDev/rollup-plugin-import-cdn" rel="noopener noreferrer"&gt;plugin&lt;/a&gt; to load any external library from a CDN. This way, we can transpile the code and all its dependencies into a single file that can be executed in a separate thread using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers" rel="noopener noreferrer"&gt;Web Workers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Running the code in a separate thread is important, as it allows us to avoid blocking the main thread. This way, the UI is always responsive, and you can continue writing code while the previous code is being executed. Also, it provides some built-in security, as the code is isolated from the main thread.&lt;/p&gt;

&lt;p&gt;Also, to improve the experience when writing TypeScript code, we switched to the fork of monaco-editor that &lt;a href="https://www.typescriptlang.org/play" rel="noopener noreferrer"&gt;TypeScript Playground&lt;/a&gt; uses. This fork, &lt;a href="https://www.typescriptlang.org/dev/sandbox/" rel="noopener noreferrer"&gt;TypeScript Sandbox&lt;/a&gt;, is always up to date with the latest version of TypeScript, and includes several improvements for TypeScript developers, such as twoslash inlay hints.&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%2Fgo7y29h016f6mi6jnfyw.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%2Fgo7y29h016f6mi6jnfyw.png" alt="Xata Playground after a UI redesign" width="800" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding support for multiple files and languages
&lt;/h2&gt;

&lt;p&gt;The first version of the Playground only supported a single TypeScript file. This was a limitation that we wanted to remove. So when we started working on it, we also decided to add support for other languages.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://microsoft.github.io/monaco-editor/" rel="noopener noreferrer"&gt;Monaco&lt;/a&gt; is the open source editor that powers VS Code, and it has support for multiple files and languages. We just needed to use its virtual file system that would allow us to load multiple files and execute them separately.&lt;/p&gt;

&lt;p&gt;The refactor allowed us to add support for SQL, both for full file execution and for inline execution. This was a great addition, as it allowed us to query data with &lt;a href="https://xata.io/blog/sql-over-http" rel="noopener noreferrer"&gt;SQL over HTTP&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding support for Python
&lt;/h2&gt;

&lt;p&gt;The next step was to add support for Python. We wanted to have the same experience as with TypeScript and SQL, where you could write code and execute it in the browser. Luckily, there are several projects that allow you to run Python in the browser, and we decided to use &lt;a href="https://pyodide.org" rel="noopener noreferrer"&gt;Pyodide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pyodide is a project started by Mozilla that allows you to run Python in the browser using WebAssembly. It includes the standard library and several popular packages like NumPy, Pandas, or Matplotlib. It also includes a package manager that allows you to install any other package with a wheel available on PyPI.&lt;/p&gt;

&lt;p&gt;Similarly to TypeScript, we have a web worker that executes the Python code. The code is executed by Pyodide, and we load external packages from imports using the package manager.&lt;/p&gt;

&lt;p&gt;To make it fully work with our Python SDK, we only needed to patch the runtime so that Pyodide could do HTTP requests, using &lt;code&gt;pyodide-http&lt;/code&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%2F4ivrf3942hs3gg4jo2yc.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%2F4ivrf3942hs3gg4jo2yc.png" alt="Current Xata Playground with Python support" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;The Xata Playground is a great tool to try out Xata's SDKs and to learn how to use them. You can quickly try out some ideas and go back to your IDE to implement them. If you haven't already, give it a try!&lt;/p&gt;

&lt;p&gt;We hope you enjoy the new Python support, and we're looking forward to seeing what you build with it. If you have any feedback or ideas, please let us know on &lt;a href="https://discord.com/invite/kvAcQKh7vm" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>data</category>
      <category>database</category>
      <category>python</category>
      <category>browser</category>
    </item>
    <item>
      <title>Discover the Xata SDK for Python</title>
      <dc:creator>Joan</dc:creator>
      <pubDate>Tue, 10 Oct 2023 12:42:26 +0000</pubDate>
      <link>https://dev.to/xata/python-sdk-for-xata-4fjf</link>
      <guid>https://dev.to/xata/python-sdk-for-xata-4fjf</guid>
      <description>&lt;p&gt;At the start, Xata's initial focus was to make things better for developers using &lt;a href="https://xata.io/blog/jamstack-mern-lamp-stack-comparison#jamstack-and-databases" rel="noopener noreferrer"&gt;Jamstack&lt;/a&gt;. This led to us creating content that predominantly focused on providing a robust &lt;a href="https://www.npmjs.com/package/@xata.io/client" rel="noopener noreferrer"&gt;TypeScript offering&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Motivated by our enthusiasm for connecting with developers, we soon launched a Python SDK, though it initially had just basic capabilities. Since that initial release, a few things have changed. The world has gone AI-crazy and Python has unofficially become the language of choice for AI/ML. This has coincided with our introduction of vector embeddings and the integration of OpenAI's ChatGPT for your data.&lt;/p&gt;

&lt;p&gt;As the Python SDK user base has grown steadily over the last few months, we have actively gathered feedback about what users like and dislike.&lt;/p&gt;

&lt;p&gt;Version &lt;a href="https://pypi.org/project/xata/1.0.0/" rel="noopener noreferrer"&gt;1.0.0&lt;/a&gt; of our &lt;a href="https://xata.io/docs/python-sdk/overview" rel="noopener noreferrer"&gt;Python SDK&lt;/a&gt; is available. &lt;/p&gt;

&lt;h2&gt;
  
  
  PEP-8 FTW!
&lt;/h2&gt;

&lt;p&gt;Previously, we received feedback that the SDK didn't feel pythonic! Initially, in the &lt;code&gt;0.x&lt;/code&gt; releases, the API was generated one-by-one from &lt;a href="https://xata.io/docs/rest-api/contexts" rel="noopener noreferrer"&gt;our OpenAPI specification&lt;/a&gt;, which resulted in a non-pythonic API. For this release, we have aligned as much as possible with the &lt;a href="https://peps.python.org/pep-0008/" rel="noopener noreferrer"&gt;PEP-8&lt;/a&gt; standard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Speed improvements
&lt;/h2&gt;

&lt;p&gt;Under the hood, we've made adjustments to how connections are managed and reused. The refactoring has yielded significant performance improvements across the board. The most notable ones include accelerated operation speeds:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Speedup (on average)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Get a single record&lt;/td&gt;
&lt;td&gt;5.95x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insert a single record&lt;/td&gt;
&lt;td&gt;4.95x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insert 100 records with transactions&lt;/td&gt;
&lt;td&gt;2.22x&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How to migrate to 1.x?
&lt;/h2&gt;

&lt;p&gt;Migrating to a new major version can be tough due to the need to understand breaking changes and features that are no longer available. With that in mind, our goal was to minimize the impact while still implementing necessary changes for the greater good. You can check out the &lt;a href="https://xata.io/docs/python-sdk/migration-guide" rel="noopener noreferrer"&gt;full migration guide&lt;/a&gt; in our docs.&lt;/p&gt;

&lt;p&gt;The most impactful user-facing change is the renaming of the API surface [&lt;a href="https://github.com/xataio/xata-py/issues/93" rel="noopener noreferrer"&gt;xata-py#93&lt;/a&gt;]. Additionally, some API endpoint calls were simplified to remove unnecessary code bloat.&lt;/p&gt;

&lt;p&gt;Previously in &lt;code&gt;0.x&lt;/code&gt;, you needed to investigate the payload shape and be specific about the region and branch name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;xata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;databases&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;createDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;new_db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;region&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us-east-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;branchName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In version &lt;code&gt;1.0.*&lt;/code&gt;, we've redesigned the API interface to incorporate payload options directly into the method signature. We've also optimized the SDK's internal values to make assumptions to reuse the SDK internal values, like the &lt;code&gt;region&lt;/code&gt;. This results in a more streamlined API structure, and you can achieve functionality using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;xata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;databases&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;new_db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What happens to 0.x?
&lt;/h2&gt;

&lt;p&gt;We will continue providing support for the &lt;code&gt;0.x&lt;/code&gt; version of the Xata Python SDK through maintenance releases for an additional year. During this time, new API enhancements and security fixes will be introduced; however, no backports of helpers or other improvements are planned. The &lt;code&gt;0.x&lt;/code&gt; SDK version will be sunsetted by September 1st, 2024.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s next?
&lt;/h2&gt;

&lt;p&gt;Check out our &lt;a href="https://xata.io/docs/python-sdk/migration-guide" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; and let us know what you think. We’d love to hear from you! If you think something is missing or you found a bug, open a ticket in the &lt;a href="https://github.com/xataio/xata-py" rel="noopener noreferrer"&gt;xata-py&lt;/a&gt; repository. All contributions are welcome. You can also follow us on &lt;a href="https://twitter.com/xata" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or join us in &lt;a href="https://xata.io/discord" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Is there a language you wished we supported natively but don't today? Feel free to open up a &lt;a href="https://xata.canny.io/feature-requests" rel="noopener noreferrer"&gt;feature request&lt;/a&gt; or check-in with our amazing community. The &lt;a href="https://xata.io/blog/community-spotlight-xata-go-sdk" rel="noopener noreferrer"&gt;xata-go SDK&lt;/a&gt; was initiated by &lt;a href="https://github.com/kerdokurs" rel="noopener noreferrer"&gt;Kerdo&lt;/a&gt; and is hosted on &lt;a href="https://github.com/kerdokurs/xata-go" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. &lt;a href="https://github.com/mrkresnofatih" rel="noopener noreferrer"&gt;mrkresnofatih&lt;/a&gt; also added the &lt;a href="https://github.com/mrkresnofatih/terraform-provider-xata" rel="noopener noreferrer"&gt;terraform-provider-xata&lt;/a&gt; to provision Xata. If you're interested in contributing, feel free to reach out with any questions! Happy building 😄&lt;/p&gt;

</description>
      <category>python</category>
      <category>database</category>
      <category>sdk</category>
      <category>data</category>
    </item>
    <item>
      <title>Using the LangChain Integration with a Serverless Database</title>
      <dc:creator>Joan</dc:creator>
      <pubDate>Tue, 10 Oct 2023 12:25:56 +0000</pubDate>
      <link>https://dev.to/xata/try-the-xata-langchain-integration-1knk</link>
      <guid>https://dev.to/xata/try-the-xata-langchain-integration-1knk</guid>
      <description>&lt;p&gt;Xata has integrations with LangChain, and is available both as a vector store and a memory store.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is LangChain?
&lt;/h2&gt;

&lt;p&gt;LangChain is a popular open-source framework for developing AI applications powered by Large Language Models (LLMs). You can think of it as a collection of composable components implemented in Python and TypeScript that you can combine to implement various AI use cases.&lt;/p&gt;

&lt;p&gt;The components typically offer a common API for different &lt;a href="https://js.langchain.com/docs/modules/model_io/models/llms/" rel="noopener noreferrer"&gt;models&lt;/a&gt; (OpenAI, Llama, Replicate, etc.), &lt;a href="https://js.langchain.com/docs/modules/data_connection/vectorstores/" rel="noopener noreferrer"&gt;vector stores&lt;/a&gt; (Pinecone, Weaviate, Chroma, etc.), databases as &lt;a href="https://js.langchain.com/docs/modules/memory/" rel="noopener noreferrer"&gt;memory stores&lt;/a&gt; (DynamoDB, Redis, Planetscale, etc.) and more. By offering a common API across different models, for example, LangChain makes it easy to switch between models and compare results or use different models for different parts of the app.&lt;/p&gt;

&lt;p&gt;These components can then be “&lt;a href="https://python.langchain.com/docs/modules/chains/" rel="noopener noreferrer"&gt;chained together&lt;/a&gt;” in more complex applications, and LangChain comes with off-the-shelf implementations for several popular &lt;a href="https://js.langchain.com/docs/use_cases" rel="noopener noreferrer"&gt;AI use cases&lt;/a&gt; (Q&amp;amp;A chat bots, summarization, autonomous agents, etc).&lt;/p&gt;

&lt;p&gt;While Xata has a built-in API for the &lt;a href="https://xata.io/chatgpt" rel="noopener noreferrer"&gt;“ChatGPT on your data” use case&lt;/a&gt;, in this blog post we’ll see how one can implement similar functionality using LangChain and Xata integrations. This can allow for more flexibility in the details of the implementation (for example, you can chose a non-OpenAI model) at the cost of having more code to write and maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  The integrations
&lt;/h2&gt;

&lt;p&gt;Currently, the following &lt;a href="https://dev.to/docs/integrations/langchain"&gt;integrations&lt;/a&gt; are available :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Xata as a &lt;a href="https://python.langchain.com/docs/integrations/vectorstores/xata" rel="noopener noreferrer"&gt;vector store in LangChain&lt;/a&gt;. This allows one to store documents with embeddings in a Xata table and perform vector search on them. The integration takes advantage of the &lt;a href="https://xata.io/blog/announcing-the-python-sdk-ga" rel="noopener noreferrer"&gt;Xata Python SDK&lt;/a&gt;. The integration supports filtering by metadata, which is represented in Xata columns for the maximum performance.&lt;/li&gt;
&lt;li&gt;Xata as a &lt;a href="https://js.langchain.com/docs/modules/data_connection/vectorstores/integrations/xata" rel="noopener noreferrer"&gt;vector store in LangChain.js&lt;/a&gt;. Same as the Python integration, but for your TypeScript/JavaScript applications.&lt;/li&gt;
&lt;li&gt;Xata as a &lt;a href="https://python.langchain.com/docs/integrations/memory/xata_chat_message_history" rel="noopener noreferrer"&gt;memory store in LangChain&lt;/a&gt;. This allows storing the chat message history for AI chat sessions in Xata, making it work as “memory” for LLM applications. The messages are stored in a Xata table.&lt;/li&gt;
&lt;li&gt;Xata as a &lt;a href="https://js.langchain.com/docs/modules/memory/integrations/xata" rel="noopener noreferrer"&gt;memory store in LangChain.js&lt;/a&gt;. Same as the Python integration, but for TypeScript/JavaScript.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each integration comes with one or two code examples in the doc pages linked above.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The four integrations already make Xata one of the most comprehensive data solutions for LangChain, and we’re just getting started!&lt;/strong&gt; For the near future, we’re planning to add custom retrievers for the Xata keyword and hybrid search and the Xata &lt;a href="https://xata.io/docs/typescript-client/ask" rel="noopener noreferrer"&gt;Ask AI&lt;/a&gt; endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why choose Xata?
&lt;/h2&gt;

&lt;p&gt;As we’ve pointed out above, a key benefit of LangChain is that it supports a lot of solutions for each integration type. For example, at the moment, the Python LangChain integrates with 47 (!) vector stores, while LangChain.js integrates with 24 vector stores.&lt;/p&gt;

&lt;p&gt;So what makes Xata different and why should you consider it for your AI apps?&lt;/p&gt;

&lt;p&gt;Xata is a serverless data platform that stores data in PostgreSQL, but also replicates it automatically to Elasticsearch. This means that it offers functionality from both Postgres (ACID transactions, constraints, etc.) and from Elasticsearch (full-text search, vector search, hybrid search) behind the same simple serverless API.&lt;/p&gt;

&lt;p&gt;Here is why you should consider Xata for you LangChain application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It’s &lt;strong&gt;comprehensive:&lt;/strong&gt; It offers LangChain integrations not only as a vector store, but also as a memory store. Also, it offers the same integrations for both the Python and TypeScript/JavaScript versions of LangChain. Because it uses Elasticsearch behind the scene, it can offer BM25 and hybrid search in addition to just vector search.&lt;/li&gt;
&lt;li&gt;It’s a &lt;strong&gt;pure serverless solution:&lt;/strong&gt; You simply get an API endpoint, no clusters or instances to configure, because we handle the scaling. The lightweight TypeScript SDK runs in any serverless environment, including Cloudflare Workers.&lt;/li&gt;
&lt;li&gt;It has a &lt;strong&gt;modern developer workflow&lt;/strong&gt;: Xata's workflow is based on branches and has built-in integrations with platforms like GitHub, Vercel, and Netlify.&lt;/li&gt;
&lt;li&gt;It’s &lt;strong&gt;easy:&lt;/strong&gt; The Xata UI makes it very easy to manage your schema, look-up data, create and test queries and searches, and generally understand what’s going on.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to get started?
&lt;/h2&gt;

&lt;p&gt;To get started with Xata and LangChain, you can use the minimal code samples from each of the integrations above. If you are looking for more complex examples, for Python, there is a more complete example in this &lt;a href="https://python.langchain.com/docs/integrations/memory/xata_chat_message_history#conversational-qa-chain-on-your-data-with-memory" rel="noopener noreferrer"&gt;Jupyter Notebook&lt;/a&gt;. For TypeScript, check out the announcement blog post on the &lt;a href="https://blog.langchain.dev/xata-x-langchain-new-vector-store-and-memory-store-integrations/" rel="noopener noreferrer"&gt;LangChain blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While the integrations added so far already make Xata one of the most comprehensive and easy to use data solutions for LangChain and AI applications, this is only the beginning! We’re planning to add more components that take advantage of Xata’s BM25 search and the Ask endpoint.&lt;/p&gt;

&lt;p&gt;If you have any questions or ideas or if you need help implementing Xata with LangChain, reach out to us on &lt;a href="https://xata.io/discord" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; or join us on &lt;a href="https://twitter.com/xata" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>database</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
