Skip to content
Jonathan's Page

App Updates

Commentslife, tinker, tech, software, apps4 min read

App Screenshot

Updated Dashboard in Dark Mode

Since the last posting, I've gotten the itch to play with a different backend and tweak the navigation a bit. I was using Firebase services for a lot of the heavy lifting. Firestore for data storage/ACLs, Cloud Functions for “serverless” backend tasks, as well as their authentication service. I had dabbled a bit with their real-time database for app caching, and I had some Remote Config. These all needed to be replicated if I wanted to migrate off and use something else.

Speaking of which, I wanted to shift to a self-hosted Postgres database. It can natively handle JSON blobs, which would come in handy when I needed to redesign the schema. It also has notifications and very flexible functions & stored procedures. I had heard Postgres can be your entire backend 😜, so that was my initial goal.


Data

Firestore is a NoSQL document storage database. So I did not have tables in mind when first building the data model. However, it all translated into fairly normal tables, and my ACLs distilled to simple Row-Level policy rules. Thanks in part to PostgreSQL’s JSON column type! 😂

Postgres is cool and all, but it should not be publicly accessible, so I wanted to put it behind a gateway. Ideally, something that could generate an API or GraphQL schema from my database schema. I would have loved a GraphQL option, but I found Postgrest and Supabase’s postgrest.js and thought… that will do. 😂 Funny enough, my original Firestore queries and [Postgres.js] queries were structured similarly… not exactly swappable, but close enough that the code using them didn’t need to really change much.

Auth

I couldn’t find a way to use only Postgres to handle OAuth flows, and the idea of building my own (again1) didn’t seem palatable… So I played with a few off-the-shelf options. I started with Auth0… mostly because I had heard of them and was curious about what they provided… However, after exploring their offerings, I felt like I would have traded one cloud service for another… and in the hour or two I spent exploring it, I did not find a convenient way to sync it with Postgrest so it could validate the JWT.

Since my app is built on Expo’s framework, I looked to their documentation for more suggestions. Their first suggestion was Better Auth. Now THIS is what I was looking for. Self-Hosted. Integrated. User definitions and data stay in my database. The only (minor) downside was that I needed to build my own service with it…

Bun

With auth/data sorted, I needed a way to get notifications from [PostgreSQL] back to the client, as well as a “node” app to host the Better Auth middleware. I decided to use Bun to be my runtime (and package manager) for these two facets. Bun has a built-in web server API that handles websockets. The only dependencies I needed to add were a JWT library and a Postgres client. As for the auth bit, I only needed the same libraries as for the rest, plus Express and the Better Auth libraries. Another neat thing Bun can do is COMPILE everything needed to run your app into a single binary. Like bundling, but even better.

To keep things simple and separated by concern, I made each facet its own service, running in its own containers. And I tied them all together with nginx as the main reverse proxy.

Footnotes

  1. I’ve built many authentication forms/flows/apps/services over the course of my career.

© 2026 by Jonathan's Page. All rights reserved.
Theme by LekoArts