theeze.dev
bfam,  Development

Building bfam: Getting My Hands Dirty (Part 2)

Date Published

beam - Logo

In my last post, I talked about why I'm building bfam and the tech stack I settled on: React, PocketBase, and Docker. Now it's time to actually build the thing.

I want to walk you through the first real development work of setting up the database, building authentication, and learning some hard lessons along the way. Not just the "what," but the why. Because honestly, I made a lot of decisions that might seem weird at first, and I definitely struggled with some stuff that should have been simple. But that's real development, right?

First Steps: Setting Up PocketBase

Remember how I said PocketBase was this beautiful all-in-one backend? Well, it's time to put that to the test. PocketBase works with "collections" which are basically tables in your database. But instead of writing SQL or managing migrations manually, you create them through a nice web UI. Then PocketBase automatically generates migration files for you.

This is perfect for my "keep it simple" goal. I can iterate quickly, and when I'm ready to ship, the migrations are already there.

The Database: Current Architecture

Here's the blueprint (which is subject to change after I finish my coffee) I settled on for the bfam MVP:

1. Users (Built-in Auth Collection)

PocketBase comes with a special users collection that handles all the auth stuff. Passwords are automatically hashed, email verification works out of the box, and you get JWT tokens for free. I just added a few custom fields:

  • isAdmin — Some family members need mod powers.
  • invitedBy — The specific user ID who created the account.

2. Posts (The Heart of Everything)

1{
2 author: Relation<User>;
3 content: string;
4 postType: 'text' | 'image' | 'video' | 'audio';
5 media: File[];
6 mentions: Relation<User>[];
7 pinned: boolean;
8}

I'm supporting multiple post types from the start. Text posts, images, videos, and even audio messages (great for grandparents who don't like typing).

3. Comments (Threaded Conversations)

1{
2 author: Relation<User>;
3 post: Relation<Post>;
4 content: string;
5 parentComment?: Relation<Comment>; // Threading!
6}

That parentComment field is the key. It allows for nested replies, keeping the architecture simple but the UX powerful.

4. Reactions

I basically copied Facebook's reaction system here (like, love, laugh, etc.). There's no shame in "borrowing" good UX that people already understand.

5. User Contacts

This is a flexible collection where users can add multiple contact methods (phone, WhatsApp, etc.). Instead of a fixed profile, users can add as many as they need. Five email addresses? Weird, but okay.

Bridging the Gap: Type Safety

One of my biggest "wins" was setting up pocketbase-typegen to read my PocketBase schema and generates TypeScript interfaces. This means my editor autocompletes fields perfectly, and if I change a database column, my frontend code "breaks" immediately so I can fix it before it ever hits production.

1npm run pb:typegen


Now, instead of guessing what's in a PostsRecord, I get full IntelliSense. This one step alone has saved me hours of debugging "undefined" errors.

Pro-Tip: In PocketBase, the media field is handled as a File during upload, but the generated types will return a string[] (the filenames). Keeping track of that distinction is vital for your frontend logic!

For New Developers Reading This

If you're starting a project, remember: Start simple. I started with just users and posts. Everything else, threaded comments, reactions, contact lists, came after the core was solid. Read the documentation, break things, and don't be afraid to "steal" UX patterns that work.

The code for bfam is open source on GitHub. Feel free to poke around and see how it works!