Using Google Sheets as a simple database with Papa Parse
Tags: engineeringmonday, javascript, papaparse, spreadsheets
Published: 2021-03-15 09:23:18

## Inspiration

On a recent meeting, a fellow dev commented on a piece of software he saw as being "just like Windows 95". This brought up some nostalgia inside me and I quickly remembered there are several libraries available for recreating that classic look and feel.

One of the first results was <a href="https://github.com/React95/React95" target="_blank">React95</a> and the first link inside the showcase was <a href="https://github.com/ggdaltoso/95Recipes" target="blank">a recipe app</a> from one of the library's contributors. The readme stated it used Google Sheets as a database, which I found interesting and decided to investigate further.

## Tabletop

The promising library user in the aforementioned example was [Tabletop](https://github.com/jsoma/tabletop). I was excited while reading their [npm](https://www.npmjs.com/package/tabletop) page, but the Github readme stated how the package is getting deprecated by Google's changes. It's a shame since that package seemed to offer many utility features for listing data from spreadsheets out of the box (including reading data from multiple sheets). Luckily, they provided an alternative (albeit much more general-purpose, but still usable for **simple** examples).
The alternative in question is...

## Papa Parse

Upon first look, [Papa Parse](https://www.papaparse.com/) seems to be a general-purpose CSV parser with a no-nonsense quick-start documentation that is readable at a glance. The parser will return JSON data which can be used for any purpose required, e.g.:
* creating tables
* visualization via charts
* iterating through data to create blocks (by using a templating engine)

## Creating a data source

The premise is simple: create a new Google Spreadsheet, enter the headers (column names) and fill the table with data (rows).

Next, we need to publish the worksheet: go to File -> Publish to the Web.

![Publishing to the web](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xwl42r1xs8nd6kriu1wz.jpg)

Make sure to click the **Start publishing** button. We need to parse CSV files, so we need to pass the link to CSV option to our code. If we only have a single sheet, this should suffice. However, if there are multiple sheets, we need to get links to each one by selecting them from the dropdown (and making sure CSV is selected).

![Copying each sheet's CSV link](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1ses0b6d5h81w7t7w8l4.jpg)

## Example

To demonstrate one way of using Google Sheets as a data source, we will use 2 sheets and a [Codepen repo](https://codepen.io/aduda091/pen/NWbLogo). The data is simple: I listed some movies and shows I've watched recently. Data will be shown inside two tables with minimal styling. To simplify HTML generation, Vue.js will be used.

The Sheet is available [here](https://docs.google.com/spreadsheets/d/13eQrrEF6ASPHsMk2Mbigrsqtj2vZt7eedTZuEQRKUgo/edit?usp=sharing) and consists of two sheets: Movies and Shows. They have almost the same structure, with the only difference being Shows' Last watched season replacing Movies' Year column.

I followed the instructions above to get separate URLs for each sheet's CSV file and saved them to variables.
```javascript
const moviesUrl =
  "https://docs.google.com/spreadsheets/d/e/2PACX-1vTtKbv_S8Fdo3HLhm64Tc94WZ6FuqtzqePIjuejNFJxKkUvAE8JF8V2KgKoz1n5jQUDfL8A3F-QoDWk/pub?gid=0&single=true&output=csv";

const showsUrl =
  "https://docs.google.com/spreadsheets/d/e/2PACX-1vTtKbv_S8Fdo3HLhm64Tc94WZ6FuqtzqePIjuejNFJxKkUvAE8JF8V2KgKoz1n5jQUDfL8A3F-QoDWk/pub?gid=1364847678&single=true&output=csv";
```

Next, a Vue instance is created and its data is initialized to empty arrays for movies and shows.

```javascript
const app = new Vue({
  el: "#app",
  data: function () {
    return {
      movies: [],
      shows: []
    };
  },
...
```

Upon creation, Vue is told to use Papa Parse to get each CSV file and set it to its state. As a side note in this example, I used an arrow function as a callback for movies and a regular anonymous function for shows. Arrow function retained the lexical scope of *this*, while the regular function required *this* to be saved (_this_ should refer to the Vue instance in order to correctly set data)

```javascript
created: function () {
    this.fetchMovies();
    this.fetchShows();
  },
  methods: {
    fetchMovies() {
      Papa.parse(moviesUrl, {
        download: true,
        header: true,
        complete: (results) => this.movies = results.data
      });
    },
    fetchShows() {
      const _this = this;
      Papa.parse(showsUrl, {
        download: true,
        header: true,
        complete: function(results) {_this.shows = results.data;}
      });
    }
  }
```

The presentation part is very simple - a single `<div>` container for Vue instance and two tables with a template which iterates over the fetched data.

```html
<div id="app">
  <table v-if="movies.length">
    <thead>
      <tr>
        <th>Title</th>
        <th>Year</th>
        <th>URL</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="movie in movies">
        <td>{{movie.Title}}</td>
        <td>{{movie.Year}}</td>
        <td><a :href="movie.Url" target="_blank">{{movie.Url}}</a></td>
      </tr>
    </tbody>
  </table>

  <table v-if="shows.length">
    <thead>
      <tr>
        <th>Title</th>
        <th>Last watched season</th>
        <th>URL</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="show in shows">
        <td>{{show.Title}}</td>
        <td>{{show["Last watched season"]}}</td>
        <td><a :href="show.Url" target="_blank">{{show.Url}}</a></td>
      </tr>
    </tbody>
  </table>
</div>
```
Notice how the shows' Last watched season column retains spaces just like we defined inside Google Sheets.

## Conclusion

This article is purposefully simplistic and is only used as an intro to what might be possible by using Google Sheets as a data source. Its collaborative nature makes it easy to make changes which make the frontend update (similar to a very basic CMS) while retaining the ability to limit read/write access as usual.

However, there are limitations - speed, possible rate limiting and the dependency on Google to keep the CSV option available in the future. As such, this will never replace a proper database and backend combination, but can still be useful for quick prototyping and fun mini-projects, especially when the data is already available.