<?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: Blaze Rowland</title>
    <description>The latest articles on DEV Community by Blaze Rowland (@blazerowland).</description>
    <link>https://dev.to/blazerowland</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%2F183695%2Fd751ab45-e7cb-4073-978e-950f5df4e827.jpeg</url>
      <title>DEV Community: Blaze Rowland</title>
      <link>https://dev.to/blazerowland</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/blazerowland"/>
    <language>en</language>
    <item>
      <title>Creating a Node.js SQL Library</title>
      <dc:creator>Blaze Rowland</dc:creator>
      <pubDate>Tue, 08 Mar 2022 17:41:49 +0000</pubDate>
      <link>https://dev.to/blazerowland/forty-boysql-2dm8</link>
      <guid>https://dev.to/blazerowland/forty-boysql-2dm8</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/blaze-rowland/forty-sql"&gt;Project on Github&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Backstory
&lt;/h2&gt;

&lt;p&gt;This week I decided to take a step forward in my development path and create my first NPM Package. &lt;/p&gt;

&lt;p&gt;I'm an Angular developer by trade. It's my preferred front-end framework. But I have found myself wanting to branch out the past couple of years to work on React projects. I've worked on a pretty large amount of side projects in Vue, so wanted to take a swing at the newest version of React. So, I found some UI inspiration online and wanted to get to it. &lt;/p&gt;

&lt;p&gt;I knew I wanted a Node.JS backend using MySQL as the database, but I've never really enjoyed using any of the ORMs like Knex. I liked the syntax of something like Mongoose significantly more but have a small preference leaning towards relational databases. That's when I decided... Why not make my own library that has a similar syntax to Mongoose, but works with a relational database? Now I'm sure there are projects/packages out there that do what I'm wanting, but the challenge intrigued me. &lt;/p&gt;




&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Install the package from NPM
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;npm install @forty-boy/sql&lt;/code&gt; OR &lt;code&gt;yarn add @forty-boy/sql&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring your .env File
&lt;/h3&gt;

&lt;p&gt;The SQL Service that powers this package will look for these values to create a MySQL Pool for all of its queries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FORTY_HOST=&amp;lt;YOUR_DB_HOST&amp;gt;
FORTY_DB=&amp;lt;YOUR_DB_NAME&amp;gt;
FORTY_USER=&amp;lt;YOUR_DB_USER&amp;gt;
FORTY_PASS=&amp;lt;YOUR_DB_PASS&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create a Table in the Database
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async createUsers(): Promise&amp;lt;void&amp;gt; {
    const sql = new SqlService('users');
    await sql.createTableQuery([
      {
        name: 'id',
        type: 'INT',
        size: 11,
        primaryKey: true,
        autoIncrement: true,
        nullable: false,
      },
      { name: 'fullName', type: 'VARCHAR', size: 255 },
      { name: 'createdAt', type: 'DATETIME' },
    ]);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create a Relational Table
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async createProducts(): Promise&amp;lt;void&amp;gt; {
    const sql = new SqlService('products');
    await sql.createTableQuery([
      {
        name: 'id',
        type: 'INT',
        size: 11,
        primaryKey: true,
        autoIncrement: true,
        nullable: false,
      },
      { name: 'name', type: 'VARCHAR', size: 255, default: 'Test Product' },
      { name: 'price', type: 'INT', size: 11 },
      { name: 'createdAt', type: 'DATETIME' },
      {
        name: 'createdBy',
        type: 'INT',
        nullable: false,
        foreignKey: {
          referenceId: 'id',
          referenceTable: 'users',
        },
      },
    ]);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create the Table Class
&lt;/h3&gt;

&lt;p&gt;*Note: All methods from the Table class return an RXJS Observable&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class UserSchema {
  id?: number; // This is nullable for Create calls
  fullName: string;
  dateOfBirth: Date;

  constructor(id: number, fullName: string, dateOfBirth: Date) {
    this.id = id;
    this.fullName = fullName;
    this.dateOfBirth = dateOfBirth;
  }
}

type UserDateset = Array&amp;lt;UserSchema&amp;gt;;

export class UserTable extends Table&amp;lt;UserSchema&amp;gt; {
  constructor(tableName: string, users: UserDataset = []) {
    super(tableName, UserSchema, users);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All Tables take in the name of the table as an argument. This allows for the use of a generic Schema for tables with the same properties, but different names if that use case ever occurs.&lt;/p&gt;

&lt;p&gt;If this is not a use case you foresee, you can set a default value in the constructor, or negate the need to supply one at all and just pass it into the super call.&lt;/p&gt;

&lt;p&gt;Tables can also include a set of default values; this can be used for testing or any use case you could find for this. The second argument is a list of default values to start the Table off with.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create an instance of the newly created UserTable
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const userTable = new UserTable('users') 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add Values to a Table
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;userTable.add({
  fullName: 'Blaze Rowland',
  dateOfBirth: new Date(1997, 11, 14),
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Find Values from a Table
&lt;/h3&gt;

&lt;p&gt;Find all values that match the condition. Returns &lt;code&gt;Observable&amp;lt;Array&amp;lt;YOUR_SCHEMA&amp;gt;&amp;gt;&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;userTable
  .find({
    columns: ['id', 'fullName'],
    condition: { id: 1 },
  })
  .subscribe((users) =&amp;gt; console.log(users));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Find one value that matches the condition. Returns &lt;code&gt;Observable&amp;lt;YOUR_SCEHMA&amp;gt;&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;userTable
  .findOne({
    columns: ['id'],
    condition: {
      fullName: 'Blaze Rowland',
    },
  })
  .subscribe((user) =&amp;gt; console.log(user));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update Values
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;userTable
  .update({
    values: { fullName: 'Some New Name' },
    condition: { id: 1 },
  })
  .subscribe((res) =&amp;gt; console.log(res));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Find and Update Values
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;userTable
  .findOne({
    columns: ['id'],
    condition: {
      id: 1,
    },
  })
  .subscribe({
    next: (user) =&amp;gt;
      userTable
        .update({
          values: { fullName: 'A Much Newer Name' },
          condition: { id: user.id },
        })
        .subscribe((res) =&amp;gt; console.log(res)),
  });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Find and Add to a Relational Table
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Find and Add to Relational Table
userTable
  .findOne({
    columns: ['id'],
    condition: {
      fullName: 'A Much Newer Name',
    },
  })
  .subscribe({
    next: (user) =&amp;gt; {
      productTable
        .add({
          name: 'Television',
          price: 600,
          createdAt: new Date(),
          createdBy: user.id,
        })
        .subscribe((res) =&amp;gt; console.log(res));
    },
  });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Delete from a Table
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;productTable.delete({ id: 1 });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Join Tables
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  productTable
    .join({
      joinType: 'INNER JOIN',
      columnsToSelect: [
        { column: 'name' },
        { column: 'price' },
        { column: 'fullName', as: 'userName', table: userTable.tableName },
        { column: 'dateOfBirth', table: userTable.tableName },
      ],
      columnsOn: [
        {
          from: { column: 'id', table: userTable.tableName },
          to: { column: 'createdBy', table: productTable.tableName },
        },
      ],
    })
    .subscribe((res) =&amp;gt; console.log(res));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  productTable
    .join({
      joinType: 'LEFT JOIN',
      columnsToSelect: [
        { column: 'name' },
        { column: 'price' },
        { column: 'fullName', as: 'userName', table: userTable.tableName },
        { column: 'dateOfBirth', table: userTable.tableName },
      ],
      columnsOn: [
        {
          from: { column: 'id', table: userTable.tableName },
          to: { column: 'createdBy', table: productTable.tableName },
        },
      ],
    })
    .subscribe((res) =&amp;gt; console.log(res));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  productTable
    .join({
      joinType: 'RIGHT JOIN',
      columnsToSelect: [
        { column: 'name' },
        { column: 'price' },
        { column: 'fullName', as: 'userName', table: userTable.tableName },
        { column: 'dateOfBirth', table: userTable.tableName },
      ],
      columnsOn: [
        {
          from: { column: 'id', table: userTable.tableName },
          to: { column: 'createdBy', table: productTable.tableName },
        },
      ],
    })
    .subscribe((res) =&amp;gt; console.log(res));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Union Tables
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;userTable
  .union({
    queries: [
      {
        columns: ['id', 'fullName'],
        tableName: 'users',
      },
      {
        columns: ['id', 'name'],
        tableName: 'products',
      },
    ],
    all: true, // Changes whether Union statement is UNION (false || not provided) or UNION ALL (true)
  })
  .subscribe((res) =&amp;gt; console.log(res));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Things I'd like to implement in the future
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A CLI Package for migrations&lt;/li&gt;
&lt;li&gt;A Migration Manager&lt;/li&gt;
&lt;li&gt;Pagination at its root.

&lt;ul&gt;
&lt;li&gt;Pagination is important for any scalable project. I think having it built in from the ground up would be great. This project is still in infancy (released just two days ago).&lt;/li&gt;
&lt;/ul&gt;


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




&lt;p&gt;This is more of a hobby project to get myself making blog posts, using my Github account, and branching out as a developer. Most my projects are private and that's not something I'm very happy about. If you want to contribute to this project I'd love to see Pull Requests or Open Issues that I can review or work on. Thanks for taking the time out of your day and I hope this package can simplify some work for you on your next project!&lt;/p&gt;

&lt;p&gt;To test this package I've been working on this &lt;a href="https://github.com/blaze-rowland/shopping-cart"&gt;project&lt;/a&gt;. As I find bugs or features that should be implemented while using this as my only means to query the database I'll create issues that will make it into future versions.&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>sql</category>
    </item>
  </channel>
</rss>
