DEV Community

Cover image for The Project That Quietly Died While I Wasn't Looking
John Wandera
John Wandera

Posted on

The Project That Quietly Died While I Wasn't Looking

Summer Bug Smash: Smash Stories 🐛🛹

This is a submission for DEV's Summer Bug Smash: Smash Stories powered by Sentry.

The project

I built Kenya Tours, a tour booking site at kenya coast tours, so people could book local tours online. Nothing huge. A Next.js frontend, Supabase behind it for the database, auth, and storage. The kind of project you build, launch, feel good about, and then life happens.

And life happened. I started a new job. It was the kind of role where you're learning a stack of unfamiliar systems fast and putting out fires on production infrastructure that isn't yours to lose, so your own side projects quietly slide down the priority list. I knew, somewhere in the back of my head, that Supabase pauses free-tier projects after enough inactivity. I'd read that warning before. I just didn't think about it again for months.

The moment I noticed

I visited the live site one day (I don't even remember why, probably just checking in) and the tours weren't loading. Empty list where a catalog should be.

That's the specific kind of dread where you already half-know what happened before you've confirmed it. I went straight to the Supabase dashboard.

The project hadn't just paused. It had been permanently removed.

What "permanently removed" actually means

There's no dramatic error message for this. It's almost worse than that. It's just... gone. No project in the list. No one-click "restore" button, because that only exists inside a 90-day window, and I was well past it.

What I did have: a downloadable pg_dump backup of the database, plus a separate storage backup zip containing the couple of images the site used. That's it. Supabase treats those as two completely different kinds of data. The SQL backup captures your schema and rows, but it has zero knowledge of file bytes sitting in a storage bucket. If you want your images back, you're re-uploading them by hand.

So the actual task wasn't "restore a project." It was: build a brand new Supabase project from scratch, and manually reassemble the old one inside it, piece by piece.

Rebuilding, piece by piece

I linked the new project with the Supabase CLI and pushed migrations built from the recovered schema: tables, RLS policies, the handle_new_user trigger, storage bucket configuration. That part went about as smoothly as CLI tooling ever does, which is to say almost, but not quite. I hit a genuinely strange one along the way. Every file inside the zip I'd downloaded on Windows had a companion file next to it, silently created by Windows itself to mark it as "downloaded from the internet." The CLI kept skipping migration files with a warning I didn't understand until I went looking for it. Harmless once you know what it is, baffling the first time you see it.

Loading the actual recovered data turned into its own small discovery. The Supabase CLI has no command to run an arbitrary .sql file against a remote database, despite there being a command for seemingly everything else. The real path was pasting the insert statements directly into the dashboard's SQL editor, and not forgetting the setval call that resyncs the auto-increment counter afterward. Skip that, and the app starts colliding IDs the next time it tries to insert a row normally.

Then the two tour images, re-uploaded by hand, filenames matched exactly so the image_url columns in the recovered data would still resolve.

By the time I had auth providers reconnected, redirect URLs reconfigured, and environment variables re-pointed at the new project, the site was back. Genuinely back: tours loading, bookings working.

Writing it down so it never costs me this much again

The whole process was rough enough, with enough small "wait, why doesn't this work" moments, that I documented every step into a README as I went: CLI setup, migrations, the seed-loading workaround, storage re-upload, env vars, auth provider setup, all of it. Not just for this project, but as a reusable reference for any future project I set up the same way, restore or not.

That felt like the real fix. The database is back, but the actual bug was me not having a process. Next time, restoring won't mean re-learning all of this from zero.

The second bug hiding behind the first

I thought I was done. Then Google Places API calls started returning a 403.

I went through everything I could think of: checked which APIs were enabled, checked the billing dashboard, convinced myself something in the migration had broken a config. The actual answer was almost anticlimactic. I'd originally set the whole project up on a Google Cloud free trial, and that trial had quietly expired. Places API (New) and Distance Matrix API both need an active billing account with credits behind it, trial or not. I've since added a card, but haven't completed the payment yet.

The specific symptom is subtler than "maps don't work." There's a location input where typing something like "Malindi" is supposed to trigger an autocomplete dropdown of suggestions. With the 403, that dropdown just never fires. And it gets stranger depending on environment: in dev, the input still loads fine, but in production it doesn't render at all. That difference comes down to how the component mounts, using Next.js's useEffect to initialize the autocomplete widget client-side after hydration. Dev and production handle that timing just differently enough that the same broken API call produces two different visible failures.

Where it stands

The site is live. Bookings work. Tours display. But the location autocomplete is still broken in production, and I know exactly why, which honestly feels different from the first bug. That one I found by accident, days or weeks after it started. This one I understand completely, and I'm just choosing when to close it.

If I'm honest about what I actually learned: the scariest bugs aren't the ones that throw an error. They're the ones that fail silently while you're busy looking somewhere else. The only real defense is either checking on the things you own more often than you think you need to, or building something that checks for you.

Top comments (0)