Listifi tech stack

January 25, 2021 on Eric Bower's blog

Given I recently launched listifi, I thought it would be interesting to talk briefly about the listifi tech stack.

Tech stack

I’d be remiss if I didn’t use my app before discussing my list of stack choices. Here is my tech stack.

In general, when I build an app I tend to focus on gluing the pieces together myself. This is my personal preference, but I find that frameworks or libraries that try to do too much end up getting in my way very quickly.

Provide some helper utilities and then get out-of-my-way.

The overall folder/file structure follows the patterns I described in my previous blog article.

Front-end

There are some common choice in my list, in particular: typescript and react. I also opted to use a component library chakra-ui primarily because it made it easy to make CSS changes by using react component props. It also wasn’t a massive library that tried to solve every design or UX problem users would come across.

One potentially controversial choices was reaching for redux. It seems as of late redux has fallen slightly out-of-favor and more people are migrating towards react-specific libraries like recoil. I spent some time researching recoil and ultimately decided it did not fit into my personal design choices. In particular, I don’t like how tightly coupled recoil is to react. I find this tight coupling makes it an easier API to work with, but will inevitably lead to issues if I wanted to build a react-native mobile app or a CLI app. Since redux is framework agnostic, battle-tested, and using libraries like robodux I can avoid 90% of the boilerplate.

robodux is great because it promotes the idea that redux is just a local database. I can create database tables which translate to slices in the redux world.

Building a redux slice that has: action types, action creators, and a reducer with a common set of table operations like: set, add, patch, and remove can be written in a single line of code:

import { createTable } from `robodux`;

interface List {
  id: string;
  name: string;
  ownerId: string;
}

const slice = createTable<List>({ name: 'LISTS' });
/*
{
  actions: {
    add,
    set,
    remove,
    patch
  },
  reducer,
  getSelectors,
}
*/

redux-cofx is another library I wrote that is a hybrid between redux-saga and redux-thunk. Instead of wiring generator functions up to sagas, I simply create a function like thunks that activate a generator function and still leverage the API of redux-saga. For example, if I want to fetch some lists, I would write something like this:

import { select, call, batch, createEffects } from 'redux-cofx';
import { selectHasTokenExpired } from '@app/token';
import { apiFetch } from '@app/fetch';

// API is very similar to redux-saga
export function* onFetchLists() {
  const hasTokenExpired = yield select(selectHasTokenExpired);
  if (hasTokenExpired) {
    return;
  }

  const resp: ApiFetchResponse<ApiListsResponse> = yield call(
    apiFetch,
    '/lists',
  );

  if (!resp.ok) {
    return;
  }

  const users = processUsers(resp.data.users);
  const lists = processLists(resp.data.lists);
  // this dispatches multiple actions at the same time without
  // two copies of the state being generated
  yield batch([
    addLists(lists),
    addUsers(users)
  ]);
}

// This is a helper function that will
// convert a map of action creator names to effects.
// When we dispatch `fetchLists` it will
// activate `onFetchLists`: e.g. store.dispatch(fetchLists());
export { fetchLists } = createEffects({
  fetchLists: onFetchLists
});

It’s a very useful little library that is a satisfying hybrid between redux-saga and redux-thunk and I encourage anyone who feels like redux-saga is too heavy for their uses to give it a try.

Backend

On the backend I decided to go with koa. I found the minimalist approach of the library to be aesthetically pleasing and exactly what I want from a web server. Koa doesn’t even come bundled with a router, you have to install one yourself, I love that! koa has a great middleware system, adopted from express.

I originally went with prismajs but ultimately found the library is too limited and restrictive. I would highly recommend people use it if they are using graphql, but for a simple RESTful API, I found it couldn’t do even the simplest of SQL queries.

So, in the end, I switched to knexjs which, again, plays right into my preferences. It’s a query builder. When I think about how to query my data, I really just want to write SQL.

As an aside, I really do not get the fascination with ORMs. SQL is already a DSL, why are we re-inventing the wheel and adding another layer of abstraction? SQL is amazing and more people should be comfortable writing in it.

I ended up writing my own server-side rendering implementation for react. All in, with data loading and getting data loaded into redux I wrote about 300 lines of code. Once I landed on a working implementation, the rest was pretty straight-forward. However, I get why people don’t want to keep rebuilding SSR over and over again and end up switching to something like nextjs.

Deployment

For deployment I tend to lean heavily on docker. I use docker-compose for development and docker-machine for deployment. Since this was a fresh project that I don’t know how far I’m going to take it, I didn’t want to create an automated build pipeline using CI. I’ll briefly describe my deployment lifecycle:

My VM was on Google’s Cloud Compute and my domain was hosted on Cloudflare.

Conclusion

This covers a high-level overview of my tech stack and something I will continue to reach for on new projects. It fits nicely into my development style and I understand how all the pieces work together because I wrote the glue myself.


Articles from blogs I read

Generated by openring

A finger client

This is a short follow-up to the io_uring finger server article posted about a month ago. In the time since, we have expanded our language with a more complete networking stack, most importantly by adding a DNS resolver. I have used these improvements to wri…

via Drew DeVault's blog June 24, 2021

The Go Collective on Stack Overflow

Since the earliest days of Go, Stack Overflow has been a significant part of the Go user experience. For the past five years, the Go user survey has consistently identified Stack Overflow as the #1 place users go to find answers to their…

via The Go Programming Language Blog June 23, 2021

How to use React Context effectively

I've posted a new article "How to use React Context effectively" and you can read it online. How to create and expose React Context providers and consumers …

via Kent C. Dodds Blog RSS Feed June 5, 2021