Creating a microblog using Vue + Supabase
Tags: javascript, vue, webdev, tailwindcss
Published: 2021-01-24 19:39:18

---
title: Creating a microblog using Vue + Supabase
published: true
description: How to create a microblog using Vue and Supabase (an open-source Firebase alternative).
tags: javascript, vue, webdev, tailwindcss
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/n2rjw0n673bf2bzj6rs1.jpg
---

At the beginning of the year, I was reading some articles about new tools in the *frontend* world, when I came across [Supabase](https://supabase.io/). I confess that my first thought was "more of the same", and this thought remained until I found this article: [Supabase raises $6M for its open-source Firebase alternative](https://techcrunch.com/2020/12/15/supabase-raises-6m-for-its-open-source-firebase-alternative/?guccounter=1).


After reading the article, the thought became, "If Mozilla is investing in this project, it is because they realized how promising it could become!". It was then that I decided to read the documentation and try to explore a little more.

Supabase is an open-source Firebase alternative for those who don't know. It includes support for PostgreSQL databases and authentication tools, with storage and serverless solution coming soon.

**How to adventure into something new using the knowledge I already have?**

It was my thought when I decided to explore, and my idea was to create a microblog using [*Vue*](https://vuejs.org/) + [*Supabase*](https://supabase.io/).

## How to start?

First, I created a new project using [Vue CLI](https://cli.vuejs.org/guide/creating-a-project.html), included [Vue Router](https://router.vuejs.org/installation.html) to manage the routes of the application and [TailwindCSS](https://tailwindcss.com/) to add some basic style to the blog without wasting a lot of time. At the end of it, my directory structure was this:

![Initial directory structure](https://dev-to-uploads.s3.amazonaws.com/i/cktt8ws8rplb5vhhgjpo.png "Initial directory structure")

> I will stick to explaining the technology integration part (Vue + Supabase) and ignoring the reasons for having created an architecture that way or about different approaches to doing the same thing.

## Configuring the database

After accessing the [Supabase dashboard](https://app.supabase.io/), which I had previously explored, I followed the guide it presents and created **my organization** and **my project**. I named them, respectively, as `supabase-test` and `supabase-vue`.

Followed by the creation of both, we're able to connect between the project and our application. Still, I decided to create my table, called `posts`, and add some fictional data to have information to consume in addition to the columns.

As you can see in the image below, the interface is friendly. Allowing us to browse the sidebar quickly, see what organization and project we are in (this information is present in the top left of the _dashboard_), and mainly configure our table or insert, edit, or even delete records in a straightforward way.

![Viewing my posts table](https://dev-to-uploads.s3.amazonaws.com/i/eyizwmgj2mu6ootzam4h.png "Viewing my posts table")

## Structuring the responsibility of the components

As I mentioned above, I used *TailwindCSS* to add basic styles and avoid wasting time. My purpose was to test the interaction between technologies and not build something so beautiful.

In the image of our structure, it was possible to see some files and components. We will detail the responsibility of each one of them:

**Directory `src/components`:**

- `Footer.vue` - It is the global _footer_ of our application. It will present my name, two icons with links to my social networks, and a simple copyright text.
- `Header.vue` - It is the global *header* of our application. It will present the project name, and on the home page, it will display a container with the blog title.
- `List.vue` - This component is the application's initial route, called `posts-list`. It is responsible for searching the information in our database and making a *loop* of the `ListItem` component.
- `ListItem.vue` - It is responsible for presenting three pieces of information, referring to each publication. They are: Title, description, and a button that will take us to the publication page.
- `Post.vue` - The second route of our application, named `post-item`, is the one that presents the complete information of each publication.
- `App.vue` - It is the component responsible for calling the other components.

**Directory `src/*`:**

- `main.js` - The default file, suggested by _Vue_, to start our application.
- `router.js` - The route file presents the basic settings for our two routes and a fallback so that any invalid _URL_ redirects to the main route.

This was the essential list with the responsibilities of each file. Let's see how it turned out in practice?

![Viewing the rendered component list](https://dev-to-uploads.s3.amazonaws.com/i/mes2dpmxge3loih4c4qz.jpeg "Viewing the rendered component list")

## Installing dependencies and configuring the application's connection to the database

The first thing to do is install the dependency. As we are in a project that uses _JavaScript_, we will use an isomorphic library for the connection between our database and the project maintained by the *Supabase* team, called [supabase-js](https://github.com/supabase/supabase-js) and which offers a very simple and straightforward [documentation](https://supabase.io/docs/client/supabase-client).

```shell
npm install @supabase/supabase-js
```

Installation completed. It's time to import the library and call a method, passing the data from our database (*URL* and *KEY*) and we will have immediate access.

```js
import { createClient } from '@supabase/supabase-js'

// Create a single supabase client for interacting with your database
const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key')
```

I decided to do this in the simplest way possible¹, I created a file called `/src/Database.service.js` with the following code:

1. **Important note:** I know this _service_ could be a _singleton_ to avoid problems, but, as I said above, the idea is to explore more about the integration between technologies.

```js
import { createClient } from '@supabase/supabase-js'

export default class DatabaseService {
    constructor() {
        this.database = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_SECRET_KEY)
    }

    getInstance() {
        return this.database;
    }
}
```

## Using the connection and fetching all posts

With our file that initializes the connection configured, use the database instance and create a method to fetch the posts.

Our `List.vue` file will be responsible for searching the data and passing the information of each _publication_ to the `ListItem.vue` component.

```js
<template>
  <section class="px-2 pt-16 pb-6 bg-white md:px-0">
    <div class="container items-center max-w-6xl px-8 mx-auto xl:px-5">

      <template v-for="(post, index) in posts">
        <list-item :key="`post-${index}`" :id="post.id" :title="post.title" :description="post.description" />
      </template>

    </div>
  </section>
</template>

<script>
import ListItem from "./ListItem";
import DatabaseService from "../Database.service";

export default {
  name: "List",
  components: {
    ListItem,
  },
  data: () => ({
    posts: [],
    database: null,
  }),
  created() {
    const database = new DatabaseService();
    this.database = database.getInstance();
  },
  async mounted() {
    await this.fetchPosts();
  },
  methods: {
    async fetchPosts() {
      const { error, data } = await this.database
        .from("posts")
        .select()
        .order("id");
      
      if (error) {
        console.error(error)
        return
      }

      this.setPosts(data)
    },
    setPosts(posts) {
      this.posts = posts;
    }
  },
};
</script>
```

In summary, the component above, during its creation, calls our _service_, which opens the connection to the database, while in the `mounted` we call the `fetchPosts` method, responsible for fetching all data from the table _posts_ ordered by _id_ (order: ASC).

After fetching the data, we add it to the _posts_ array. Our DOM will react to the change in the viable one and iterate over it, passing the correct information to each `ListItem` component.

----

At the same time, our `ListItem.vue` component, responsible for rendering each of the publications on our list, follows this structure:

```js
<template>
  <div class="flex flex-wrap items-center sm:-mx-3 mt-12">
    <div class="w-full">
      <div class="w-full pb-6 space-y-6 lg:space-y-8 xl:space-y-9 sm:pr-5 lg:pr-0 md:pb-0">
        <h1
          class="text-4xl font-extrabold tracking-tight text-gray-900 sm:text-5xl md:text-4xl lg:text-5xl xl:text-4xl"
        >
          <span class="block xl:inline">{{ title }}</span>
        </h1>
        <p
          class="mx-auto text-base text-gray-500 sm:max-w-md lg:text-xl md:max-w-full"
        >{{ getShortDescription }}</p>
        <div class="relative flex flex-col sm:flex-row sm:space-x-4">
          <router-link
            :to="{ name: 'post-item', params: { title, description } }"
            class="flex items-center w-full px-6 py-3 mb-3 text-lg text-white bg-indigo-600 rounded-md sm:mb-0 hover:bg-indigo-700 sm:w-auto"
          >
            Read the article
            <svg
              xmlns="http://www.w3.org/2000/svg"
              class="w-5 h-5 ml-1"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <line x1="5" y1="12" x2="19" y2="12" />
              <polyline points="12 5 19 12 12 19" class />
            </svg>
          </router-link>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "ListItem",
  props: {
    title: String,
    description: String,
  },
  computed: {
    getShortDescription() {
      return `${this.description.substr(0, 100)}...`;
    },
  },
};
</script>
```

The first important thing to note is that we receive two parameters: `title` and `description`, these are the data that were previously sent for each _post_, and will be rendered as follows (as we have already seen in the previous image).

![One of the items in the list of publications displayed on the first page of the application](https://dev-to-uploads.s3.amazonaws.com/i/n78edo2tb8webcgwiog3.png "One of the items in the list of publications displayed on the first page of the application")

----

Another detail that we must take into account is the link of the button **Read the article**, which uses the component `<router-link>` provided by _Vue Router_ and which allows us to direct the user to the route called `post-item `, also sending two parameters: `title` and `description`.

The route called `post-item`, will load the component `Post.vue`, showing the received parameters (_title_ and _description_). Let's look at the code and the visual result:

```js
<template>
  <div
    class="flex items-center justify-center py-10 text-white bg-white sm:py-16 md:py-24 lg:py-32"
  >
    <div class="relative max-w-3xl px-10 text-center text-white auto lg:px-0">
      <div class="flex flex-col w-full md:flex-row">
        <div class="flex justify-between">
          <h1 class="relative flex flex-col text-6xl font-extrabold text-left text-black">
            {{ $route.params.title }}
          </h1>
        </div>

      </div>

      <div class="my-16 border-b border-gray-300 lg:my-24"></div>

      <p
        class="text-left text-gray-500 xl:text-xl"
      >
      {{ $route.params.description }}
      </p>
    </div>
  </div>
</template>

<script>
export default {
  name: "Post",
};
</script>
```

![Publication preview page](https://dev-to-uploads.s3.amazonaws.com/i/k61d7r2x00d48d0ud5ai.png "Publication preview page")

So far, everything has been relatively simple and useful for the proposed experiment.

----

**What is the final result of our application visually?**

![Navigability of the microblog](https://dev-to-uploads.s3.amazonaws.com/i/l1faw4e0l9cve67nzd34.gif "Navigability of the microblog")


## Conclusions and the future

Although our application has been entirely experimental and we have not explored all the features of _Supabase_. It is possible to notice enormous ease, both in _Supabase's dashboard_ and in the interaction between the application and the database.

_Supabase_ is quite recent, but it looks extremely promising. It currently offers the possibility of creating a backend in less than 2 minutes with _Postgres Database_, _Authentication_, _instant APIs_, and _realtime subscriptions_. Soon it will still give us _Storage and serverless functions_, therefore, keep your eyes and ears open for news about this project.

If you are interested in the source code of the project we created, go to the [project directory on GitHub](https://github.com/ftonato/vue-supabase-microblog) and implement even more exciting things, such as:

- **Realtime Subscriptions** - To show a new publication whenever it is inserted in the database ([I also wrote an article on how to do this](https://dev.to/ftonato/realtime-subscriptions-using-vue-supabase-1e11)).
- **Admin panel** - An area reserved for the blog administrator to insert, edit or delete publications using the _Authentication_ functionality.
- Pagination of publications on our main page.
- Etc.

In summary, there is a multitude of things that can be done. Let your imagination guide you and when you are finished, share with us <3

If you have any question, do not hesitate to contact me (_ademilsonft@outlook.com_ / [@ftonato](https://twitter.com/ftonato)) or the [Supabase team](https://supabase.io/docs/support)).