Kawartha Lakes Food Source
Full-Stack Nonprofit Platform — Public Site, Serverless API & Admin CMS
Sole Developer, AI-Assisted (Full-Stack)
- Delivered three coordinated systems—public site, serverless API, and a custom admin CMS—as a solo, AI-assisted build
- Went from kickoff to launch-ready in record time, with very few hiccups along the way
- Replaced manual content edits with a model-driven CMS: new content types ship with zero router or component changes
Overview
Kawartha Lakes Food Source (KLFS) coordinates a network of member food banks across the City of Kawartha Lakes. Their web presence needed more than a redesign—it needed a public site, a serverless API behind it, and a real content management system so a small, non-technical staff could run the site themselves instead of filing a ticket every time a slide or a board bio needed updating.
I built all three, solo, leaning heavily on AI-assisted development. Not "type a prompt and ship whatever comes back"—more like having a very fast pair programmer who still needed direction on architecture, security, and the parts that actually matter. The result: a production-ready platform delivered in record time, with very few hiccups along the way.
My role
This was a true end-to-end, one-person build. I owned:
- Public-facing site redesign and rebuild (Vue 3)
- Serverless API design—rebranded and extended from patterns I'd already proven on my earlier Coboconk & Area Food Bank project, rather than starting from a blank slate
- A brand-new internal admin CMS so staff can manage slides, programs, news, partners, donors, stats, staff/volunteer/board bios, and the food bank directory themselves
- Auth0-backed authentication and role-based permissions for admin users
- AWS infrastructure, deployments, and the shared content data model, end-to-end
What I built
A public site staff don't need a developer for
The old workflow meant every homepage change ran through me. The new site is entirely content-driven: homepage slides, programs, news and events, partner logos, featured donors, impact stats, food bank operating dates, and staff/volunteer/board bios all come from a shared content API instead of being hardcoded into the site.
A serverless backend built to stay cheap
The API is a single Express service running on AWS Lambda behind API Gateway, backed by DynamoDB. I didn't design this from zero—I took the backend I'd already built and proven for the Coboconk & Area Food Bank site and rebranded and extended it for KLFS's larger, multi-location content model. Reusing a working foundation instead of reinventing one is a big part of how this shipped as fast as it did.
An admin portal built for exactly the traffic it gets
This is an internal tool for one to four staff at a time, so I optimized every decision for near-zero idle cost over scalability it will never need. Content types are model-driven—adding a new one is a schema definition, not a rewrite—and every record uses soft delete only; there's no "permanently remove this" button anywhere in the UI.
Auth0 handles login with Authorization Code + PKCE, and four granular permissions (read/create/update/deactivate) gate what each admin user can actually do. Every mutating action writes a structured audit log line, so there's always a trail of who changed what.
Image uploads support both direct file upload and fetch-from-URL, with server-side validation on file size, MIME type, and pixel dimensions—and the URL-fetch path is hardened against SSRF, since it's a server making requests based on user input. The admin also meets a WCAG baseline: skip links, semantic landmarks, labeled inputs, live-region status messaging, and an automated accessibility check that runs before every release.
Built AI-assisted, on purpose
I want to be direct about this one: I used AI-assisted development heavily across the frontend, backend, and admin CMS, and it's a major reason this went from kickoff to launch-ready as fast as it did. That doesn't mean it was hands-off. I still made every architectural call—reusing the CAFB backend instead of rewriting it, keeping the admin deliberately minimal instead of over-engineering for scale it doesn't need, deciding where auth, validation, and audit logging had to be non-negotiable.
It wasn't entirely friction-free. Sharing one DynamoDB table across a public site, its API, and a new admin surfaced a couple of schema quirks I had to reconcile, and locking down the asset-fetch-by-URL feature against SSRF took a more careful pass than the first draft. But nothing that put the launch date at risk—which is really the point: AI-assisted doesn't have to mean corners cut, it can just mean less time spent typing boilerplate and more time spent on the decisions that actually matter.
Technical approach (high level)
Three services, each scoped to what it actually needs to do:
- Public site: Vue 3 + Vite, deployed as static assets on S3/CloudFront
- Public API: Express.js on AWS Lambda behind API Gateway, backed by DynamoDB
- Admin frontend: Vue 3 + TypeScript (strict) + Vite, Pinia, Auth0 SPA SDK, hand-rolled semantic HTML/CSS, Zod for runtime validation
- Admin backend: a plain Node.js ESM Lambda handler with manual routing—no Express needed for four endpoints—verifying JWTs against Auth0's JWKS and enforcing RBAC from the token's permissions
- Data: a single shared DynamoDB table, read by the public API and managed through the admin CMS
- Infrastructure & deployment: Serverless Framework services for hosting and API, deployed from a local machine via a single idempotent pipeline—no CI/CD or long-lived cloud keys required for a project this size