<?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: Graham Vasquez</title>
    <description>The latest articles on DEV Community by Graham Vasquez (@gv14982).</description>
    <link>https://dev.to/gv14982</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%2F469771%2Fe285b9e3-7637-4835-895b-ddf4312acd96.jpeg</url>
      <title>DEV Community: Graham Vasquez</title>
      <link>https://dev.to/gv14982</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gv14982"/>
    <language>en</language>
    <item>
      <title>How to setup GitHub Actions on your Github repository</title>
      <dc:creator>Graham Vasquez</dc:creator>
      <pubDate>Mon, 09 Nov 2020 19:14:10 +0000</pubDate>
      <link>https://dev.to/gv14982/how-to-setup-github-actions-on-your-github-repository-53bg</link>
      <guid>https://dev.to/gv14982/how-to-setup-github-actions-on-your-github-repository-53bg</guid>
      <description>&lt;p&gt;Writing software is pretty fun, but there are lots of menial tasks that can get annoying. Things like testing, handling deployments, and other small tasks can take away from the fun of writing code. That's why you should automate those tasks!&lt;/p&gt;

&lt;h1&gt;
  
  
  But what are Github Actions?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/features/actions" rel="noopener noreferrer"&gt;Github Actions&lt;/a&gt; is the built in automation feature on Github. There are other third-party solutions such as &lt;a href="https://circleci.com/" rel="noopener noreferrer"&gt;CirclCi&lt;/a&gt; and &lt;a href="https://travis-ci.org/" rel="noopener noreferrer"&gt;TravisCi&lt;/a&gt; which follow the same general idea.&lt;/p&gt;

&lt;p&gt;To start with Github Actions, you first need a Github repository! In this tutorial we are going to be working with my existing project, &lt;a href="https://github.com/GV14982/async-airtable" rel="noopener noreferrer"&gt;AsyncAirtable&lt;/a&gt;, &lt;small&gt;If you want to know more, check out my write up.&lt;/small&gt; &lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/gv14982" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&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%2Fuser%2Fprofile_image%2F469771%2Fe285b9e3-7637-4835-895b-ddf4312acd96.jpeg" alt="gv14982"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/gv14982/making-intneracting-with-the-airtable-api-even-easier-1lm5" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Making interacting with the Airtable API even easier&lt;/h2&gt;
      &lt;h3&gt;Graham Vasquez ・ Nov 1 '20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#npm&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#node&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#database&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;h1&gt;
  
  
  Alright, well how do I set them up?
&lt;/h1&gt;

&lt;h4&gt;
  
  
  Directory structure
&lt;/h4&gt;

&lt;p&gt;So to get started with github actions you need to create a &lt;code&gt;.github&lt;/code&gt; folder in your repository. Within that folder you want to create a &lt;code&gt;workflows&lt;/code&gt; folder. So your repo file structure 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;repo/
├── .github/
│   └── workflows
└── ...code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cool, so now that we have the folder setup, lets take a look at our workflow files.&lt;/p&gt;

&lt;h4&gt;
  
  
  Workflow yaml files
&lt;/h4&gt;

&lt;p&gt;Github Action workflow files are written in &lt;a href="https://yaml.org/" rel="noopener noreferrer"&gt;yaml&lt;/a&gt;, which is a super easy to read and write. So let's take a look at a workflow yaml file and then break it down!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: Tests

on:
  push:
    branches:
      - develop
  pull_request:
    branches:
      - master

jobs:
  test:
    strategy:
      max-parallel: 1
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]

    runs-on: ${{ matrix.os }}

    steps:
      - uses: actions/checkout@v2
      - name: Setup Node
        uses: actions/setup-node@v1
        with:
          node-version: 12
      - name: Install
        run: npm install
      - name: Run tests
        run: npm test
        env:
          AIRTABLE_KEY: ${{secrets.AIRTABLE_KEY}}
          AIRTABLE_BASE: ${{secrets.AIRTABLE_BASE}}
          AIRTABLE_TABLE: 'tests'
          TEST_FILTER: "{email} = 'same@test.com'"
          NEW_RECORD: '{"title": "test-create", "value": 23, "email": "new@test.com"}'
          UPDATE_RECORD: '{"title": "test-UPDATED"}'
          DESTRUCTIVE_UPDATE_RECORD: '{"title": "test-UPDATED-destructive", "value": 23}'
          RETRY_TIMEOUT: 60000
          REQ_COUNT: 100

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

&lt;/div&gt;



&lt;h4&gt;
  
  
  Let's break this down!
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;h5&gt;name&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;name&lt;/code&gt; field is just used to name the workflow on Github.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;h5&gt;on&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;on&lt;/code&gt; field is used to express &lt;em&gt;when&lt;/em&gt; you want this workflow to run.&lt;/li&gt;
&lt;li&gt;In this case I have it running every time code is pushed to my develop branch, or a pull request is created on my master branch.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;h5&gt;jobs&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;jobs&lt;/code&gt; field specifies the jobs you want to run in the workflow.&lt;/li&gt;
&lt;li&gt;The jobs are listed by name. In this case, my job is called &lt;code&gt;test&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;h5&gt;job&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;For each job you need to specify some information. This information includes:&lt;/li&gt;
&lt;li&gt;
&lt;h5&gt;runs-on&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;The operating system the job runs on.&lt;/li&gt;
&lt;li&gt;In this case I specified a matrix that is an array of operating systems to test my code on all 3 major operating systems. I also make it so only one job can be run at a time using the &lt;code&gt;max-parallel&lt;/code&gt; field. (This is due to a constraint with the API I'm talking to 😅)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;h5&gt;steps&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;These are the commands the job will actually run.&lt;/li&gt;
&lt;li&gt;These can either be terminal commands or other actions you can reference.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;uses&lt;/code&gt; field is used to denote a reference to another published github action. In this case I am use the checkout action that loads the code from my repository into the workflow, and a node setup action.&lt;/li&gt;
&lt;li&gt;For the other steps I am just running my npm terminal commands, and specifying &lt;code&gt;env&lt;/code&gt; or &lt;code&gt;environment variables&lt;/code&gt; for my code to reference.&lt;/li&gt;
&lt;li&gt;I want to point out the &lt;code&gt;${{secrets.SECRET_NAME}}&lt;/code&gt; entries I have in the &lt;code&gt;env&lt;/code&gt;. These are secrets you can add to your Github repository. In this case, they are my API key and base name. Check out more &lt;a href="https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets" rel="noopener noreferrer"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  Wrap-up
&lt;/h1&gt;

&lt;p&gt;And that is the basics of a workflow file for Github Actions. If you want to know more, be sure to check out the &lt;a href="https://docs.github.com/en/free-pro-team@latest/actions" rel="noopener noreferrer"&gt;Github Actions Docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Feel free to reach out to me if you have any questions and I hope you enjoy automating all those lame tasks 😊&lt;/p&gt;

</description>
      <category>github</category>
      <category>tutorial</category>
      <category>devops</category>
    </item>
    <item>
      <title>Making interacting with the Airtable API even easier</title>
      <dc:creator>Graham Vasquez</dc:creator>
      <pubDate>Sun, 01 Nov 2020 21:51:41 +0000</pubDate>
      <link>https://dev.to/gv14982/making-intneracting-with-the-airtable-api-even-easier-1lm5</link>
      <guid>https://dev.to/gv14982/making-intneracting-with-the-airtable-api-even-easier-1lm5</guid>
      <description>&lt;p&gt;I am not sure how many of you are familiar with &lt;a href="https://airtable.com/" rel="noopener noreferrer"&gt;Airtable&lt;/a&gt;, but it's an easy to use pseudo database that you can manage like Excel. It supports tons of ways to store different types of data, and allows you to make links between records ala RDBMS. I use it all the time for quick prototyping, or in places where database performance is not a priority and I want an easy way to manage data outside of my application.&lt;/p&gt;

&lt;p&gt;They also have a fairly robust &lt;a href="https://api.airtable.com/" rel="noopener noreferrer"&gt;API&lt;/a&gt; which gives you access to your usual CRUD interface. While this API is pretty easy to use, you can always make it easier right?&lt;/p&gt;

&lt;p&gt;Enter the &lt;a href="https://github.com/airtable/airtable.js/" rel="noopener noreferrer"&gt;Airtable.js&lt;/a&gt; library from Airtable. It makes talking to their API from javascript really easy and quick. The problem is that it's call-back based 🤮. Now I know that this is more of a matter of opinion, but I love me some promises and more so async/await.&lt;/p&gt;

&lt;p&gt;Thus I built &lt;a href="https://asyncairtable.com/" rel="noopener noreferrer"&gt;AsyncAirtable&lt;/a&gt;. I just wanted a really easy way to deal with the Airtable API and keep my code nice and clean.&lt;/p&gt;

&lt;p&gt;Let's take a quick look at what it looks like to get some data using Airtable.JS"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let results = [];
base('TEST').select({})
  .eachPage(function page(records, fetchNextPage) {
    records.forEach(function(record) {
        results.push(record);
    });
    fetchNextPage();
  }, function done(err) {
    if (err) { console.error(err); return; }
    done();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Seems pretty long and verbose. How about if we want to accomplish the same thing with AsyncAirtable?&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const results = await asyncAirtable.select('TEST');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;These blocks of code will net us the same data. See how easy that is!?&lt;/p&gt;

&lt;p&gt;We have built this with all the basic functionality of the Airtable API and a few extra bells and whistles, namely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built in pagination&lt;/li&gt;
&lt;li&gt;Built in upsert method for you MySQL afficiandos.&lt;/li&gt;
&lt;li&gt;Fully typed with declaration files&lt;/li&gt;
&lt;li&gt;A handy retry feature that will retry a query if you are rate limited&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now I still have some more ideas for this project to make it even more fun to work with. Let's take a look at some of these:&lt;/p&gt;
&lt;h3&gt;
  
  
  ✨ A QUERY BUILDER ✨
&lt;/h3&gt;

&lt;p&gt;Anyone who has dealt with the Airtable API in the past is probably familiar with &lt;a href="https://support.airtable.com/hc/en-us/articles/203255215-Formula-field-reference" rel="noopener noreferrer"&gt;filter formula strings&lt;/a&gt;. If you are, you are probably just as excited about this as me. The Airtable filter formula strings can get really weird REALLY fast, and can sometimes be hard to follow.&lt;/p&gt;

&lt;p&gt;I am working on a query builder that will be more like your traditional ORM such a Sequelize. So you can go from 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;"AND({name} = 'Graham', {age} &amp;gt; 18, {hungry} = TRUE())"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now take that and make it 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;{
  $and: [
    {name: 'Graham'},
    {age: {$gt: 18}},
    {hungry: true}
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;See? Look at how nice that is!&lt;/p&gt;

&lt;p&gt;Let's put it in the context of using a Javscript library to talk to the API.&lt;/p&gt;

&lt;p&gt;Here is what that might look like with the Airtable.JS library:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let results = [];
base('TEST').select({
    filterByFormula: "AND({name} = 'Graham', {age} &amp;gt; 18, {hungry} = TRUE())"
}).eachPage(function page(records, fetchNextPage) {
    records.forEach(function(record) {
        results.push(record);
    });
    fetchNextPage();
}, function done(err) {
    if (err) { console.error(err); return; }
    done();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now let's compare that to how you would do the same thing in AsyncAirtable:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const results = await asyncAirtable.select('TEST', {
  where: {
    $and: [
      {name: 'Graham'},
      {age: {$gt: 18}},
      {hungry: true}
    ]
  }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It makes query building so much easier. Plus building it programmatically is simpler because it just uses a standard Javascript object instead of having to use messy template strings or worse, string concatenation. &lt;/p&gt;

&lt;p&gt;Another feature I am excited to be adding in an upcoming release is data modeling. It won't be as robust as the models used by ORMs like Sequelize, but it will give you some type safety and help you catch errors while you are writing your code, similar to Typescript.&lt;/p&gt;

&lt;p&gt;Thanks for reading and I hope you get a chance to test out AsyncAirtable. Be sure you check us out on Github. It is completely open source, so feel free to take a look around, and contribute!&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/GV14982" rel="noopener noreferrer"&gt;
        GV14982
      &lt;/a&gt; / &lt;a href="https://github.com/GV14982/async-airtable" rel="noopener noreferrer"&gt;
        async-airtable
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A lightweight npm package to handle working with the Airtable API.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Async Airtable&lt;/h1&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;ARCHIVED&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;I strongly suggest using the current official &lt;a href="https://github.com/Airtable/airtable.js" rel="noopener noreferrer"&gt;Airtable SDK&lt;/a&gt; as it now supports promises and I don't really have time to maintain this anymore.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/gv14982/async-airtable/actions" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/1a3e8832911d52e8b50bded92bb701359914a866a7ee5c30f249cf3cf5de1600/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f776f726b666c6f772f7374617475732f475631343938322f6173796e632d6169727461626c652f6e6578743f6c6162656c3d4e657874266c6f676f3d6a657374266c6f676f436f6c6f723d7768697465267374796c653d666c6174" alt="Build: Tests"&gt;&lt;/a&gt;
&lt;a href="https://github.com/gv14982/async-airtable/actions" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/8de707754d2a52dc6fec7290389e3eefc5a1797e6072612e075ef031062c3b46/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f776f726b666c6f772f7374617475732f475631343938322f6173796e632d6169727461626c652f6d61696e3f6c6162656c3d4d61696e266c6f676f3d6a657374266c6f676f436f6c6f723d7768697465267374796c653d666c6174" alt="Build: Tests"&gt;&lt;/a&gt;
&lt;a href="https://www.npmjs.com/package/asyncairtable" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/8c47378a1c27b99d4c07144cb3c98754f2ff0588428b3e245608030a1c1ec2bf/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f6173796e636169727461626c65" alt="npm"&gt;&lt;/a&gt;
&lt;a href="https://www.npmjs.com/package/asyncairtable" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/6a5c79eb0ce33a28a5c9d02f4bd0159a309e3368a5ff7bccf7d9265ffdce14fd/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f6173796e636169727461626c652f6e657874" alt="npm (tag)"&gt;&lt;/a&gt;
&lt;a href="https://github.com/GV14982/async-airtableLICENSE.md" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/be26c7c094b208f144e8ec768cf649d69025a5c45cd91d55ee34de8ce1ec2e89/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f475631343938322f6173796e632d6169727461626c653f7374796c653d666c6174" alt="MIT License"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;AsyncAirtable is a lightweight npm package to handle working with the &lt;a href="https://airtable.com/api" rel="nofollow noopener noreferrer"&gt;Airtable API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;They have an existing library, but it is callback based and can get a little klunky at times, so I wrote this one that is promise based to make your life easier 😊.&lt;/p&gt;
&lt;p&gt;I also wrote a query builder so, instead of having to write those really annyoying &lt;a href="https://support.airtable.com/hc/en-us/articles/203255215-Formula-Field-Reference#array_functions" rel="nofollow noopener noreferrer"&gt;filter formula strings&lt;/a&gt; you can just use an object like:&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;{
  where: {
    name: 'AsyncAirtable',
    $gte: {stars: 13}
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;which will generate the following filterFormula string for you: &lt;code&gt;AND({name} = 'AsyncAirtable', {stars} &amp;gt;= 13)&lt;/code&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Requirements&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;NodeJS&lt;/li&gt;
&lt;li&gt;npm&lt;/li&gt;
&lt;li&gt;&lt;a href="https://airtable.com/signup" rel="nofollow noopener noreferrer"&gt;Airtable account&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Installation&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Be sure get your &lt;a href="https://support.airtable.com/hc/en-us/articles/219046777-How-do-I-get-my-API-key-" rel="nofollow noopener noreferrer"&gt;API key&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To get the base ID of your new base. You can do this by…&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/GV14982/async-airtable" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Cheers 🍻 &lt;/p&gt;

</description>
      <category>javascript</category>
      <category>npm</category>
      <category>node</category>
      <category>database</category>
    </item>
  </channel>
</rss>
