Canilo is a full-stack Italian-language dog shelter adoption platform I built for Canile Sanitario Bari, a municipal shelter operating in the industrial zone of Bari. It gives the shelter a public face on the web — visitors can browse adoptable dogs, contact the staff through WhatsApp, and learn about the adoption process — and it gives the shelter staff a self-service admin dashboard for managing dog profiles, uploading photos and videos, tracking adoption status, and generating ready-to-publish Instagram captions to push dogs into the social-media stream.
The platform is deployed at canilo.alessandro-carella-lavoro.workers.dev and runs entirely on Cloudflare's free tier: Workers for the backend, KV for structured data, and R2 for media. There is no traditional server, no database license, no monthly hosting bill — a deliberate constraint, because shelters and small non-profits typically can't sustain operating costs for a website that exists to serve a public mission.
The project covers everything end-to-end: architecture decisions, the React SPA, the Hono backend, the KV/R2 data model, the admin authentication layer, the analytics pipeline, the Italian copywriting, the responsive design, the deployment automation, the internal documentation system, and the migration scripts that seeded the initial dog profiles from the shelter's Instagram archive.

This was an end-to-end solo build: I owned the product, the architecture, the implementation, and the deployment. Concretely, the work spanned:
Product definition. Translating the shelter's needs (showcase dogs, take adoption inquiries, keep up with Instagram) into a concrete feature set — public listing, dog detail page, contact funnel, admin CRUD, statistics, and the Instagram caption generator. Choosing what not to build was as important as choosing what to build: no payment integration, no in-platform messaging, no donation flow — the platform stays focused on matching visitors with dogs and routing them to WhatsApp.
Architecture. Designing a serverless data model on top of Cloudflare KV (a key-value store with no joins, no indexes, and eventual consistency), with R2 for binary media. Picking Hono as the router for its low overhead on the Workers runtime, and react-router-dom v7 with the new file-based features for the SPA shell.
Frontend. Building every public and admin page in React 19 + TypeScript, including responsive layouts, skeleton loaders, drag-and-drop photo uploads, filter pills, photo galleries with thumbnails, sticky mobile CTAs, and the Recharts-based statistics dashboard.
Backend. Writing the full Hono API surface (CRUD for dogs, file upload to R2, signed media serving, analytics counters, PBKDF2-based authentication with session tokens), all in a single Worker binary.
Operations. Setting up the Wrangler workflow, provisioning the KV namespace and R2 bucket, configuring the production variables, and wiring a pre-deploy check that runs tsc, vite build, and wrangler deploy --dry-run in sequence.
Documentation and code quality. Authoring per-folder
READMEs in src/, plus building a custom
ts-morph-powered docstring checker that fails CI if any exported
function or component is missing a JSDoc comment, and a
README-table generator that keeps the per-folder Exports tables in sync
with the code.
Content seeding. Writing a Python downloader that scrapes the shelter's Instagram archive and uses the upload API to seed the initial dog roster, so the platform launched pre-populated with real dogs rather than empty.
KV is not a database, and that's fine. Building on KV forced me to design the read path first and decide up front what every page needs, instead of relying on ad-hoc SQL queries to bail me out at render time. The dog list page reads one KV entry (a sorted index of dog IDs) and then fans out to per-dog reads — explicit, traceable, and easy to reason about.
Edge runtimes have sharp edges. The Workers runtime is not Node.js: Buffer does not exist by default, process.env is gone, and many npm packages assume APIs that aren't there. Picking small, edge-native dependencies (Hono, plain crypto.subtle for PBKDF2) avoided a class of bugs that Node-targeted libraries would have introduced.
Designing admin UX for non-technical users matters more than designing the public site. The shelter staff are the ones who use the platform every day. The Add Dog page got more iteration than any public page — file upload with reorderable thumbnails, immediate visual feedback on upload progress, sensible defaults so a profile can be created in under a minute. A clever admin interface that the staff can't use is a failed product.
Italian-language UX has its own constraints. Gender-agreeing copy (adottato vs. adottata), consistent vocabulary for personality traits, and avoiding English-borrowed words where Italian readers expect Italian — these decisions accumulate and shape the perceived quality of the platform. The personality tag vocabulary went through several revisions before landing on 14 traits that staff actually wanted to use.
Serverless lets a small project punch above its weight. A shelter with no IT budget gets the same edge-distributed, globally cached, zero-downtime infrastructure that a large e-commerce site would pay for. That asymmetry — institutional-grade tech at hobbyist-grade cost — is the most interesting thing about the project. It is the architectural pattern I'd reach for again the moment someone needs a real website with no real budget.







