Sometimes things just work, eventually

If you read my last post (here), you’ll know I’ve been experimenting a lot with supabase (a cloud-hosted database/api etc). If “a sort of detailed discussion about how I implemented a many-to-many relationship and queried it from the javascript client” doesn’t interest you, feel free to skip this one.

enjoy a public domain cat photo for your troubles…

Okay, so if you’ve gotten this far, presumably you care and know a little bit about databases and javascript. Cool, me too!

So, the scenario I’m working on is we have a list of books. These books have a bunch of information stored, like the author, title, description. As well, for organizing, searching, and other future purposes, I want the books to have ‘tags’. Basically these could be anything, maybe the book’s genre. Or maybe a special tag to show that this book was actually a book we read for book club, not just a suggestion.

Described above, we have a many-to-one relationship that may turn out to be false (books have one author, authors have 1 or more books). This is pretty easy to do in our database schema (all this code is just here for explanatory purposes, and doesn’t include a lot of things like you’d probably want in these tables such as created_by field or other such metadata).

CREATE TABLE authors (
  id int PRIMARY KEY,
  name string NOT NULL
);
CREATE TABLE books (
  id int PRIMARY KEY,
  title string NOT NULL,
  author_id int REFERENCES authors(id) NOT NULL
);

This will create our authors and books tables. The tables are linked by that author_id field, and so we can do things like query books and their authors all in one query. In SQL, you’d do that with a join. This is not the place to learn SQL. I use it a lot but I’m far from an expert and there’s some great resources for it out there.

In supabase, if we wanted to get all the books and their authors, we’d do something like this:

type Author = {
  id: number;
  name: string;
}

type Book = {
  id: number;
  title: string;
  author: Author;
}
const {data} = await supabase.from<Book>('books').select(`
  id,
  title,
  author: authors (
    id,
    name
  )
`);

This will return a list of objects of type Book. Super easy so far, and this part is well documented in the supabase client docs. What was less well documented was how to do a more complicated many-to-many join, like in the case with tags.

So the database schema for tags is pretty straighforward. First we create a tags table:

CREATE TABLE tags (
  id int PRIMARY KEY,
  name string NOT NULL
);

But this time, because a book can have many tags, and a tag can have many books, we can’t just join with a simple column on either our tags or books tables. So we do a linking table such as:

CREATE TABLE books_tags (
  book_id int REFERENCES books(id),
  tag_id int REFERENCES tags(id)
);

The records in this table act like a junction between a specific book and a specific tag. Again, in SQL you’d have to do a somewhat sophisticated join to get the list of tags on a book, or the list of books that have a given tag. But in supabase, it’s again fairly straightforward. I thought originally I’d have to do something like this because the documentation makes it seem like you have to step through the relationship explicitly. And then parse through the returned data to put tags as a direct property of a book.

const {data} = await supabase.from<Book>('books').select(`
  id,
  title,
  author: authors (
    id,
    name
  ),
  books_tags (
    book_id,
    tag_id,
    tags (
      id,
      name
    )
  )
`);

But on a whim, after a light google search, it turns out that this works:

const {data} = await supabase.from<Book>('books').select(`
  id,
  title,
  author: authors (
    id,
    name
  ),
  tags (
    id,
    name
  )
`);

Where our new Book type is as follows, without any post-query parsing:

type Author = {
  id: number;
  name: string;
}
type Book = {
  id: number;
  name: string;
}
type Book = {
  id: number;
  title: string;
  author: Author;
  tags?: Tag[]
}

So great when things “just work”! Also, let me know how these code snippets work out for you. If I end up doing more code examples, I think I’ll go with a plugin that does syntax highlighting. And also change the inline code font a bit so it’s more distinct. But open to feedback also.

I think my next post about this will be about how I’m handling authentication during development and my plans for authentication with actual users.

Latest coding experiment

Some major progress this week on the app I’ve been (very slowly) working on for my book club.

Screenshot of my in-progress book club app, showing the profile page.

Book club doesn’t have an official name, but that makes for a boring app. So it’s named after a peach pit (a story for another time) and styled after The Peach Pit (the diner in Beverly Hills 90210).

Originally, I’d started building out this app as a way to experiment with GraphQL APIs and show a prospective employer my React skills. But now that prospective employer is my actual employer, and I use GraphQL very heavily all the time. Controversial opinion (maybe?) but I don’t love it. I do think it has its place in the world of APIs but like everything it’s not the right fit all the time. And so now, the thing I mostly wanted to explore was some front-end stuff – using TypeScript with React and a new-to-me UI library – chakra-ui. So for the backend, I just really wanted to get something up and running fast.

So, I went with supabase to handle all my database, user management, and API stuff. Key factors are that it’s free, easy to get running, and handles authentication stuff. It’s been pretty painless so far. Making that banner above with html/css/svg took about me about the same amount of time as it did to get my database all setup. Which says something about my rusty front-end skills, but also says something about how easy supabase can be to get started!

You can see my code on github if that’s a thing you like to do.

Or you can subscribe to my blog over on the side if you want to hear about my thoughts on chakra-ui or more details about how I set up supabase. I have no serious timeline on this, but even in just building out a small piece of this app, I’m already learning lots and this is as good a place as any to record them for posterity (aka future-me…)

A letter to my Member of Parliament

Note: This letter contains frank discussions of Indigenous Residential Schools in Canada. I encourage you to read and listen to Indigenous people about their stories, not to just read what settlers have to say about it.

The bodies of 215 children were discovered buried under a school and the news came to light over the weekend. And now, today, I’m supposed to just be back at work like nothing has happened? With the knowledge that if the body of even one single white child was found in an unmarked grave, the whole country would be mobilizing to figure out how such an atrocity could have happened. But instead, we’re asked to wear an orange shirt or change our Facebook profile picture or lower our flags, and continue to go about our lives.

Our lives take place on land stolen from indigenous people, in a society enabled by the fact that our government (our government elected by and representing us as citizens) removed children from their homes and placed them in institutions that they called schools. My life in this place is made possible because we allowed children to be forcibly removed from their homes, institutionalized, traumatized, and killed.

If that makes you feel uncomfortable to read, well that’s because it’s an uncomfortable and awful truth about our country. We can’t truly love being Canadians until we can reckon with the ugly, awful parts of being Canadian, too.

These institutions that we call schools were run by people that felt it is better for children to be dead than to be indigenous. This is the only conclusion I can come to from this information. These children died and instead of informing their families, turning themselves in to the authorities, burying them properly, or changing their policies to prevent further deaths, the people at these schools buried the children in unmarked mass graves and continued on. And no white people noticed or cared that these children went missing.

And now the existence of one of these graves has now been proven through science, although if we’d listened to and believed survivors of this institution, we’d have already known about this. The fact that hundreds of children died while in the care of the government isn’t surprising or new information for many, many people.

I’m asking you to recommit to (and actually follow through on this commitment) implementing all of the recommendations made in the Truth and Reconciliation report. It’s a long list of calls-to-action, so can I suggest that we start with the ones involving missing children and burial information. Public awareness of this issue is high, and I desperately hope that you’re getting many, many of your constituents asking for the same.

Lowering flags or saying that you support the TRC just isn’t enough. It’s never been enough, and I’m only sorry that I’m just coming to this realization now.

As my elected representative in our federal government, I’m asking you to admit to uncomfortable truths and take action that might not be popular. But it’s our responsibility to listen to what indigenous people are asking for and to truly commit to reconciliation (in actions not just words). As settlers to this land, as a country that has done terrible things to the people who lived here before us, this is really the bare minimum that we should do to make amends.

Thank you for your time.

I have a shop?

So, as a web developer, I’ve done a lot of different types of work – small apps, big apps, mobile apps, websites, pixel-perfect CSS, adequate CSS, forms (so many forms), emails, etc. But the big thing I’ve never really even considered is e-commerce.

Until now?

Decided that I wanted to see what it’s like to try setting up a small online store, and since the whole purpose of this website is to try things and talk about them, I present to you my shop. It sells only two stickers and I’m sorry the shipping is so expensive… But this is really about me doing the experiment and less about anyone actually buying the stickers.

But if you like the ‘designs’ (I am not a designer…) and would like to purchase, that’s fun too. Or if you see the designs and say, hmm, that would make a fun t-shirt or hat or whatnot, let me know and I’ll look into adding more things to the store.

So, how did I build a very bare-bones store? I mean there’s definitely more bare-bones ways to do it, but the thing with the setup I’m about to explain is that it’s more extendable than some other store front that’s setup by a company. So here we go:

I’m using WooCommerce plugin to handle the WordPress side of things. My theme has no extra WooCommerce support, so the theming etc of the shop bits is super basic but lots of room for growth.

Then, I have a Square account to handle the credit card processing. The main thing I like about this is there’s no fees unless someone buys something. And also, there’s a Square/WooCommerce integration plugin so I just had to connect the two (and do some weird config steps that probably would have been easier if I’d fully read the docs).

And for the actual products, I’m using Printful for the manufacturing and shipping. I think if I were to more seriously have a sticker shop, I’d buy in bulk and mail myself, but that’s not what this is about. Again, there’s no costs for me until someone buys something, so it’s low risk.

Also, there’s a Printful/WooCommerce integration so I can easily sync the products and the orders. Kind of overkill for just stickers, but makes this whole process super easy to expand and extend.

So, yes, I know I started this by saying as a web dev I’d never done e-commerce, and as you’ll see above, I still haven’t really done e-commerce as a dev. Unless you expand web dev to include someone who makes things on the internet. In this case, I just used tools other than code. Although as I work on theming the WooCommerce bits, there will be some code involved, I’m sure.

So, about the designs – when I started brainstorming, I looked through my twitter bookmarks, phone notes, etc. for fun quotes that might make cute stickers. I am not a designer/illustrator/visual artist, so something text-based felt more manageable. The quote comes from a Hank Green tweet:

So I scribbled that out and then started sketching very loose ideas that might go with it, and that’s how I came up with the question mark heart.

Pulled out my rusty copy of Sketch and played around for an embarrassingly long time to make what I hope are some fairly cute stickers. I ordered them, and am excited to stick them on my laptop or water bottle.

Oh, and if you’re interested, any profits I make from the store will be donated to the Maternal Health Project in Sierra Leone that Hank and John Green are fundraising for. I figure if I’m going to borrow/steal someone’s tweet, that’s basically the least I can do….

Adventures in Book Club

Book Club apps that is…

I just did the first iteration on the book club app I’ve been thinking about for a while. It’s not really doing much aside from listing out the books we’ve read (well as many as I’ve loaded in so far). But it’s a full-stack application and I even managed to deploy in via netlify.

On the backend:

I’m using a WordPress custom post type (‘Book’) and the Advanced Custom Fields (ACF) plugin to set-up the data on the back-end. Books have authors, cover URLs, descriptions, etc. I’ve added a field for the bookclub date, but some of these we read 5-6 years ago, so I’m not worrying about going back to fill them in. On top of WordPress, I’m using the delightful WPGraphQL plugin to expose nice, convenient endpoints.

Initially, I was excited to use the WPGraphQL extension for ACF, but I realized that with a couple lines of php, I could have a nice flat book object return client-side. Surprisingly easy, if you’re curious check out the plugin repo.

On the front-end:

It’s a pretty standard React app so far, bootstrapped really quickly with create-react-app and Apollo. I did a quick proof-of-concept a couple weeks ago, just messy loading in data and making sure that it works. Again, mostly easy. You can check out the repo here.

This morning and tonight, I focused on starting from scratch with what I’d learned and writing better, or at least less lazy code. I wrote a small set of tests (in Jest) for the various components, and that was a really great learning experience. I’ve used Jest a lot at work, but from within the Salesforce ecosystem (with their lightning web components). The basics are, of course, the same, but the way to mock data retrieval is quite different.

Although now that I’ve figured out how to mock the Apollo client and GraphQL queries, I’m realizing it’s kind of the same:

  1. Initialize component
  2. Provide a mock source of data
  3. Wait for DOM updates or other async stuff
  4. Make assertions.

Basically the same steps for any unit test, just in the Apollo client vs. Salesforce the way(s) available to do #2 & #3 are pretty different.

What’s Next?

Next minor thing is being able to click on each book and read more about it. After that, display each book’s categories and allow user to view all books in a given category. Pretty standard React routing and queries.

After that, the slightly trickier part – allow authenticated users to add new books via searching the Google Books API. The GraphQL part I’m not too worried about, but authentication just adds another level of complexity.

Oh yeah… If you want to see what we’ve been reading

SSL Certificates

So this is one of those things I thought would be hard. And then it just took me about 5 minutes…

I’ve been using greengeeks as my web host for years and years, and recently they added a one-click Let’s Encrypt SSL certificate. I have a few hosted clients where the free Let’s Encrypt product might not be the right choice, but for a very small personal blog, it’s a fine choice in my opinion.

And like seriously, it took 5 minutes. Maybe would have been less, but I made a cup of tea while I was waiting for it to install. And then I came into my WordPress backend and did a couple things and poof. Cute little lock up there…

If I had a larger or messier WordPress set-up, there could have been some weird things to also do, like find-replace of hard-coded urls, but this site is really basic so far (and also doesn’t have hard-coded urls anywhere…).

So, if you’re on the fence about it, or nervous or whatever, I’d say just give it a try somewhere low stakes. I mean the worst that happens really is you uninstall the certificate and go back to normal. The best that happens is you contribute to an ecosystem where having secure websites is normal and approachable for everyone.

Progress

So it’s been a while since I’ve made development progress on my side-project. First I was in England for a few weeks, then I had family in town, and now it’s the middle of August…

But I made some good progress tonight and it’s a thing I can sort of talk about on here. The thing I want to do is an app for my bookclub to keep track of our books that we’ve read. We tried making a goodreads group, but it’s so painful to use that we basically just have a note in someone’s phone that has a list. But having actual information about the books we’ve read, and the books we want to read would be so handy!

So, in the spirit of using what I know but also learning lots along the way, the planned stack is:

  • Headless WordPress for the backend
    • custom WordPress plugin, with wpgraphql
  • React app for the frontend
    • plan will be to do an offline-first app eventually, but first iteration is just a regular old webpage with React
    • integration into Google Books API to search for and add new books

Tonight’s progress was getting a good chunk of the WordPress up and running. I’d had a bit of a false start a few months ago, doing it as a WordPress theme, and then realizing that was pretty unnecessary once I decided to do things via graphql. So this evening I was able to whip up a quick start to things: https://github.com/smallbellows/bookclub-wordpress-plugin. It’s nothing fancy at all, but it’s a start.

If you’re interested in the other details, I’m using Local by Flywheel as my local WordPress machine, which was a super easy thing to set up. I’ve used MAMP and Vagrant over the years, and they’re fine but I was super impressed with how quickly I was up and running with Flywheel.

Next up, I’ll be getting the React app going with authentication. I know if I hosted the app on the same domain as the WordPress stuff, I wouldn’t have to worry about it, but I think I’d rather keep them separate so that I can put the app wherever it makes sense as that part grows. I haven’t figured out where I’m going to host any of this yet so I want to keep my options open. Any suggestions?

Been away for a while

Yes, been away from the blog for a while, but for mostly good reasons. I went to England for a couple weeks in June and then have just been enjoying summer since then. And working! And side-projects! And podcasting!

But, I’m popping in to share what I consider to be a big win! You know when there’s the way of doing things that you know about, and then you discover a new way of doing it and your world is just blown? Yeah, that’s where I’m at today.

Been working on getting a newsletter set up for a side project I do with friends. We haven’t announced the newsletter yet, and that’s turned out to be a good thing, because it turns out that Mailchimp just was being a pain in the butt for what we wanted to do. And then today I heard about MailPoet, which is a WordPress plugin that allows you to build newsletter content right inside your WordPress site. Yes, it’s really that amazing!

I mean, we haven’t actually sent anything yet, or figured out that end of it, but in the last 40 minutes, I’ve gone from completely dreading the whole process of figuring out this newsletter to completely delighted with the whole thing. With Mailchimp, I was going to have to figure out some sort of custom RSS feed or just do a lot of copy-pasting of content across – the plan for the newsletter was a bit of a highlight of the content for the week, so just having our standard RSS feed wasn’t going to cut it. But then we’re all super busy so manually copy-pasting stuff into Mailchimp every week also wasn’t going to be a reasonable solution.

But! Then! MailPoet. I initially installed it on this site (after doing some research and reading on my commute home today), and once I realized I liked how it worked, I signed up for their free account and installed it on our side project site. From initially installing it on my site to sending out a preview of my first draft to then coming and writing this blog post, it’s been 45 minutes. And that was mostly spent picking awesome photos for the newsletter…

There’s definitely some quirks in their drag-and-drop builder, but there’s always a bit of a learning curve with any new tool. In general, it’s just such a treat to find a tool and have it actually do what you’ve been struggling with.

And shoutout to MasterWP newsletter for having them as a sponsor – again just lovely to have thing that really highlights great stuff happening in the WordPress community (and beyond).

Mentorship

Tonight, I went to a great panel/workshop called “Devs Who Mentor Devs”. There were about 20 people there, I think ranging from students to intermediate developers.

The four panelists were all former/current instructors at RED Academy, the hosts of the workshop, and had also worked as devs at a various companies. There was about an hour of panel/questions/discussions, and then we broke into small groups to talk further. And then wrapped up with a brief practice exercise in coaching.

As someone who’s being asked to mentor/coach/support junior devs at my company and also still growing and learning in my own career, it was great to talk to other people at a similar stage. I think my biggest takeaways were:

  1. Being a mentor/leader/coach/teacher is important at any stage of your career, even when (especially when) you don’t feel ready. It allows you to reflect on how far you’ve come.
  2. Having a mentor in your career, whether formal or informal, is equally important. Especially as a junior – if they’re hiring you as a junior, part of that trade-off is that they support you in your growth.

Might seem obvious, but solidifying it in my head was a really great exercise. And I got to experience both sides of a mini coaching session – both super valuable!

Developing a Theme

I am not a graphic designer, and I try not to pretend to be one… I do, however, have Thoughts about how I want my website’s layout to look and function.

So, to that end, I’m starting with a starter theme – wprig.io

Screen shot inception – at least until I make this theme mine

Right now, it’s in its starting state, I haven’t done anything with it yet other than get it running locally and install it on the live site. It’s kind of late here, and I spent a good chunk of time trying to figure out wprig 2.0 (beta). And I might go back to it, but for now, while I just want to have something working, I’m going to stick with 1.0.4 (latest release).

Reading through the code for 2.0, I realize that I’m also very out of practice with PHP. I can figure out the basics, and the structure of an older style of WordPress theme is pretty familiar to me. The class structure in wprig 2.0, though, in theory makes a lot of sense, but just wasn’t clicking with me about where to even start. Maybe once there’s documentation, I’ll feel a little more confident diving into it!

I work with classes and object-oriented programming all the time in my day job – Salesforce uses a language called Apex which is based on Java. And I use component-based architecture for front-end, as well (3 different flavours, depending on the day). So I’m confident I can figure out PHP with classes, etc. but it’s a school night and I still have a podcast to edit.