<?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: smallStall</title>
    <description>The latest articles on DEV Community by smallStall (@smallstall).</description>
    <link>https://dev.to/smallstall</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%2F823907%2F38297f42-fe7a-4036-91db-87e8a981baf6.png</url>
      <title>DEV Community: smallStall</title>
      <link>https://dev.to/smallstall</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/smallstall"/>
    <language>en</language>
    <item>
      <title>How to migrate with Knex.js locally in Supabase</title>
      <dc:creator>smallStall</dc:creator>
      <pubDate>Sun, 26 Mar 2023 06:55:24 +0000</pubDate>
      <link>https://dev.to/smallstall/how-to-migrate-with-knexjs-locally-in-supabase-4p5i</link>
      <guid>https://dev.to/smallstall/how-to-migrate-with-knexjs-locally-in-supabase-4p5i</guid>
      <description>&lt;h2&gt;
  
  
  To migrate with local Supabase
&lt;/h2&gt;

&lt;p&gt;Supabase provides a local development environment that can be used to facilitate development.&lt;br&gt;
The following are possible ways to migrate with Supabase.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SQL and PL/pgSQL&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.prisma.io/" rel="noopener noreferrer"&gt;Prisma(ORM)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://knexjs.org/" rel="noopener noreferrer"&gt;Knex.js(SQL Query Builder)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each method has its advantages and disadvantages, and in the case of the raw SQL pattern, it is better in some cases to know a little PL/pgSQL to put the seed in. For Prisma, you may need to learn the syntax and look into &lt;a href="https://github.com/supabase/supabase/discussions/7659" rel="noopener noreferrer"&gt;GitHub discussion&lt;/a&gt;. Knex.js also requires learning the syntax. For those unfamiliar with the Prisma front-end, I think Knex.js is the easiest to understand at this point, so I will introduce this method.&lt;/p&gt;
&lt;h2&gt;
  
  
  Install Supabase locally
&lt;/h2&gt;

&lt;p&gt;The documents are available.&lt;a href="https://supabase.com/docs/guides/cli/local-development" rel="noopener noreferrer"&gt;https://supabase.com/docs/guides/cli/local-development&lt;/a&gt;&lt;br&gt;
Please note that this is quite a lot of traffic to install.&lt;br&gt;
Hopefully when you access &lt;a href="http://localhost:54323/" rel="noopener noreferrer"&gt;http://localhost:54323/&lt;/a&gt; you will see a screen like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fi2de1eclmpgzr75gbcmo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fi2de1eclmpgzr75gbcmo.png" alt="Supabase Studio"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The Migration Procedure
&lt;/h2&gt;

&lt;p&gt;As a prerequisite, we will describe the procedure in case we want to add an authentication feature. If not, modify the procedure as necessary. Go to the supabase folder you created in the previous section and generate package.json.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Install pg and knex.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g knex
npm install pg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generate knexfile.js.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Once the knexfile is generated, make the following changes to the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Update with your config settings.

/**
 * @type { Object.&amp;lt;string, import("knex").Knex.Config&amp;gt; }
 */
module.exports = {
  development: {
    client: "pg",
    connection: "postgresql://postgres:postgres@localhost:54322/postgres",
    pool: {
      min: 2,
      max: 10,
    },
    migrations: {
      directory: __dirname + "/migrations",
    },
    seeds: {
      directory: __dirname + "/seeds/development",
    },
  },
};

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

&lt;/div&gt;



&lt;p&gt;The postgresql://postgres:postgres@localhost:54322/postgres part is the local postgres URL.&lt;br&gt;
Next, generate the file for migration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx knex migrate:make migration_name 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the files in the generated migration folder and edit it as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;exports.up = function (knex) {
  return knex.schema.createTable("profiles", (table) =&amp;gt; {
    table
      .uuid("id")
      .references("id")
      .inTable("auth.users")
      .primary()
      .onDelete("CASCADE");
    table.string("name", 18).notNullable;
  });
};

/**
 * @param { import("knex").Knex } knex
 * @returns { Promise&amp;lt;void&amp;gt; }
 */

exports.down = function (knex) {
  return knex.schema.dropTable("profiles");
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this file, auth.users, a table containing sensitive user information, is inaccessible to general users for security reasons. Inaccessibility means that information such as your name, handle, icon, etc. cannot be retrieved (some information such as email address can be retrieved from auth.getUser). Instead, the profiles table created in this file is used to allow general users to access user information. ↓For more details, click here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://supabase.com/docs/guides/auth/managing-user-data" rel="noopener noreferrer"&gt;https://supabase.com/docs/guides/auth/managing-user-data&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Execute the following command to perform migration up.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;knex migrate:up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Accessing &lt;a href="http://localhost:54323/" rel="noopener noreferrer"&gt;http://localhost:54323/&lt;/a&gt; will create the profiles table as follows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fthgumlug86j5kew8aj9e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fthgumlug86j5kew8aj9e.png" alt="The tables created"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate the trigger
&lt;/h2&gt;

&lt;p&gt;To link the auth.users and profiles tables, a trigger should be registered in the database so that when data is entered into the auth.users table (i.e., a new user is registered), data is entered into the profiles table.&lt;br&gt;
Add the following code to seed.sql.&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="c1"&gt;-- inserts a row into public.profiles&lt;/span&gt;
&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handle_new_user&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="k"&gt;trigger&lt;/span&gt;
&lt;span class="k"&gt;language&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;
&lt;span class="k"&gt;security&lt;/span&gt; &lt;span class="k"&gt;definer&lt;/span&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="k"&gt;public&lt;/span&gt;
&lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="k"&gt;insert&lt;/span&gt; &lt;span class="k"&gt;into&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;profiles&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;name&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="k"&gt;new&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="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raw_user_meta_data&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;'name'&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="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- trigger the function every time a user is created&lt;/span&gt;
&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;trigger&lt;/span&gt; &lt;span class="n"&gt;on_auth_user_created&lt;/span&gt;
  &lt;span class="k"&gt;after&lt;/span&gt; &lt;span class="k"&gt;insert&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;each&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt; &lt;span class="k"&gt;execute&lt;/span&gt; &lt;span class="k"&gt;procedure&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handle_new_user&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the database is initialized, run the following command to start over: supabase stop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;supabase stop
supabase start
knex migrate:up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The seed.sql is run at supabase start. If all went well, the on_auth_user_created trigger should be generated as follows.&lt;br&gt;
&lt;a href="https://media.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%2Fla8amfm78k7yxgz33b6l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fla8amfm78k7yxgz33b6l.png" alt="on_auth_user_created trigger"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Insert the seeds
&lt;/h2&gt;

&lt;p&gt;The following command is executed to create the seed file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;knex seed:make seeds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit the seed file as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const crypto = require("crypto");
const uuid1 = crypto.randomUUID();

/**
 * @param { import("knex").Knex } knex
 * @returns { Promise&amp;lt;void&amp;gt; }
 */
exports.seed = async function (knex) {
  // Deletes ALL existing entries
  await knex("auth.users").del();
  await knex("auth.users").insert({
    id: uuid1,
    raw_user_meta_data: JSON.stringify({ name: "hoge" }),
  });
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We generate a uuid and seed the auth.users and profiles tables.&lt;br&gt;
Execute 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;knex seed:run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The data is now in the tables.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;auth.users table&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fbf3qdz1sgcbbyzxg187d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fbf3qdz1sgcbbyzxg187d.png" alt="auth.users table"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;profiles table
&lt;img src="https://media.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%2Fdxd1pc2gk5jfyod6qhee.png" alt="profiles table"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we add tables, we will be able to continue the frontend development with Supabase.&lt;br&gt;
Knex.js can do less than Prisma, and type generation from models is not possible. However, the Supabase CLI &lt;a href="https://supabase.com/docs/reference/cli/supabase-gen-types-typescript" rel="noopener noreferrer"&gt;covers type generation from tables&lt;/a&gt;, so I think it would be a good match with no role conflicts. I think it is a good match. For client-side CRUD, @supabase/supabase-js is used and Knex is not used. This is to avoid SQL injection &lt;a href="https://github.com/supabase/supabase/discussions/3419" rel="noopener noreferrer"&gt;client-side SQL is controlled&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
