Custom Web Development

The Working Avo

A co-founder's build log on The Working Avo, the Singapore platform I'm building with a friend. Covering the architecture, the Django and Next.js stack, the Sanity-powered blog with structured data, and what's still on the roadmap as we keep shipping.

20264 min readVisit project
NextJsPostgreSQLStripeSupabaseNodeJsPythonDjangoPosthogVercelClerkTypescript
The Working Avo

What I built

The Working Avo is a company I co-founded with a friend. It's a Singapore-based platform offering unlimited design and development as a subscription — businesses sign up, submit requests through a dashboard (a website, a name card, a brand identity, an API integration, whatever they need), and we work through the queue and deliver the finished assets back to them inside the same dashboard.

The product itself is made up of three pieces: a marketing landing page to drive awareness and signups, a customer dashboard where subscribers create and track their requests, and an internal admin app where we manage the incoming work, update statuses, and attach final deliverables.

This is an ongoing project rather than a finished one, so this post is a snapshot of where things stand and where they're headed.

Starting with the requirements

Before writing any code, we did the same thing I always do — wrote out the functional requirements and looked at how similar businesses position themselves. Subscription-based design services are a known model overseas, but the Singapore market for design plus development under one roof felt under-served, especially for smaller teams who can't justify hiring full-time.

That research shaped the three-app structure. The landing page needed to clearly explain the model, the customer dashboard needed to make requesting and tracking work feel effortless, and the admin app needed to give us the operational tools to actually run the business as it grew.

The architecture

Frontend

The frontend is Next.js with TypeScript, deployed on Vercel. Both the customer dashboard and the marketing site live in this stack, with the marketing site optimised for SEO and the dashboard built around interactive flows.

Backend

For the backend, I went with Django and Python, hosted on Render. I wanted a proper, opinionated backend framework for this one rather than reaching for serverless functions — the business logic is going to grow over time (subscriptions, request workflows, asset management, admin tooling) and Django's batteries-included approach pays off as the surface area expands. I'm using Django Ninja Extra for the API layer, which gives me FastAPI-style typed endpoints with Django's ORM and admin underneath.

Database and storage

The database is Postgres, with Supabase handling both the database and S3-compatible file storage for project assets. The Django backend talks to both directly.

Authentication

Authentication is handled by Clerk on the frontend, with the Django side validating Clerk tokens through Django Ninja Extra's auth hooks so every protected endpoint is properly gated.

Sanity for content

Sanity is doing a lot of heavy lifting on the content side, and it's one of the parts of the stack I'm happiest with.

We've got a dedicated CMS site set up specifically for the blog, separate from the main app. This gives the team a clean, focused environment to draft, edit, and publish posts without ever touching the codebase. The schemas are tailored to how we actually write — categories, authors, hero images, rich text with embedded code blocks and callouts — so writing a new post feels closer to using a proper editorial tool than wrestling with a generic CMS.

On the published side, every blog post ships with proper JSON-LD structured data. Each article gets the full Article schema including headline, author, datePublished, dateModified, and image, plus BreadcrumbList for navigation context and Organization schema referencing The Working Avo as the publisher. The rest of the metadata is dialled in too — Open Graph tags for social sharing, Twitter Card metadata, canonical URLs, and meta descriptions pulled directly from each post's Sanity fields. Search engines and AI answer engines get a clear, structured picture of every piece of content we publish.

The blog itself is partly an SEO play and partly a way to share what we're learning publicly — but having the structured data foundation in place from day one means every post we ship contributes to the site's overall authority rather than being a one-off.

Payments and pricing

We're using Stripe for the SaaS side — subscription plans, billing, promo codes, the works. Promo codes in particular were important for early-stage growth since we want to be able to run launch offers and partner discounts without rebuilding pricing logic each time. Stripe Coupons and Promotion Codes plug straight into the checkout flow, so we can spin up a new offer in minutes.

Frontend performance

For client-side data fetching I went with TanStack Query rather than SWR this time. The dashboard has a lot of interconnected data — projects, requests, assets, statuses — and TanStack's invalidation model is genuinely better for keeping everything in sync after mutations. Submit a new request, and the relevant queries invalidate and refetch automatically. The devtools also make debugging fetches and cache state a lot less painful than guessing.

I also made a point of using WebP images across the site instead of PNGs. The marketing page is fairly visual, and WebP gives us much smaller file sizes at equivalent quality, which translates into faster loads — especially on mobile, where most discovery traffic comes from.

Where it's at

The platform is live and we've been onboarding early customers, but it's very much a work in progress. The core flows work well, the architecture is holding up, and we're learning a lot about which parts of the product actually get used versus which were nice ideas on paper.

What's next

A few things are on the roadmap.

Linear integration for ticket tracking

Right now the admin side handles request management in our own UI, but as the volume grows we want proper ticket tracking. The plan is to integrate Linear so each incoming request becomes a Linear issue, which gives us better prioritisation, sprint planning, and visibility into what's queued versus in progress. It also keeps the engineering team in tools they already use rather than forcing context-switching into our admin app.

A live notification system

I want customers to get real-time updates when their requests change status — picked up, in progress, ready for review, delivered. TanStack Query supports this nicely through query refetch intervals and refetch-on-window-focus, which gets us most of the way there without needing a full WebSocket setup. For anything that needs true push, Supabase Realtime is sitting right there in the stack already, so we can layer it in for the events that matter without changing infrastructure.

Marketing infrastructure

Once we're ready to start running paid campaigns, we'll integrate Google Tag Manager, Facebook Pixel, and the Facebook Conversions API. GTM gives us a single place to manage tags without redeploying, the Pixel handles client-side ad attribution, and the Conversions API sends server-side events for the conversions that matter most — more reliable than relying on browser-side tracking alone in a world of ad blockers and ITP.

Closing thoughts

Building The Working Avo has been a good test of putting a real product together end to end — not just the code, but the positioning, pricing, operations, and the unglamorous bits in between. The stack has held up well so far, and the choices that felt overkill at the start (Django over serverless, TanStack over SWR, a proper CMS) are already paying off as the product grows. More to come as we keep shipping.