DEV Community

Cover image for Denver's Care Card: turning messy dog notes into a shareable care card with Gemini
Bo Louie
Bo Louie

Posted on Edited on AI-assisted

Denver's Care Card: turning messy dog notes into a shareable care card with Gemini

DEV Weekend Challenge: Dog Days Edition Submission 🐕

This is a submission for Weekend Challenge: Dog Days Edition

What I Built

You're leaving town for two days and a neighbour has kindly offered to watch your dog. You start typing everything they need to know, feeding, the thunder thing, which vet, and it comes out as one long anxious paragraph that somehow still forgets the important part.

Denver's Care Card turns that messy brain-dump into a clear, shareable care card. An owner types a quick, unstructured note about their dog; the app returns an organized card, feeding, meds, walk routine, quirks, emergency contact, and a warm note about what makes the dog happy, ready to hand to whoever's stepping in.

The idea comes from Pack, a neighbourhood dog care project I spent the last 13 weeks researching and designing, about the trust between neighbours who look after each other's dogs (5-minute walkthrough here). The research and design thinking are from that project; the app itself I built this weekend for the challenge.

Demo

Try the app

Type a quick, messy note about a dog, click Generate, and you'll get a structured care card back. If the app stalls or asks you to try again, that's it waiting out a busy moment on Gemini's end, not a crash.

The app showing a generated care card for Denver with a Copy care card button

Code

GitHub logo bolouie / denvers-care-card

Turn a quick, messy note about your dog into a clear, shareable care card. React + Netlify Functions + Gemini.

Denver's Care Card

Turn a quick, messy note about your dog into a clear, structured care card worth sharing.

Live app: https://denvers-care-card.netlify.app

A weekend project built for the DEV Weekend Challenge: Dog Days Edition. I had just completed a 13-week UX design project on neighbourhood dog care (Pack) when the challenge came up, so the timing was too good to pass on: a chance to build a small, working app inspired by research I still had fresh.

What it does

When a dog owner needs a neighbour to step in on short notice, the information that matters most, feeding, meds, quirks, which vet, usually gets passed along as a rushed, disorganized text. Denver's Care Card takes that quick note and uses Google's Gemini API to return an organized care card: feeding, meds, walk routine, quirks, emergency contact, and a warm note about what makes the dog happy, ready to…

How I Built It

Keeping the API key off the client. There were two ways to connect to Gemini: call the API directly from my React code (client-side), or put a serverless function between my app and Gemini (server-side). I chose the server-side function, for one main reason: security. A React app runs entirely in the user's browser, which means the JavaScript bundle gets downloaded to their machine. If I put the API key in my front-end code, it would end up compiled into that public bundle, even if I kept it in a .env file during development. Anyone could open DevTools, watch the Network tab when they clicked "Generate care card," and lift the key. So the key lives only in the serverless function, stored as a backend environment variable. The React app sends the user's note to the function, the function attaches the key and calls Gemini, and the response comes back to be rendered in the care card. The key never touches the browser. The lesson I'm taking from this: never ship secrets to the client, especially an API key with billing attached.

Forcing the AI to return predictable data. If I just asked Gemini to "create a care card for my dog," I'd get back a chatty paragraph. But this is a React app with defined sections, my CareCard component expects seven distinct fields, so I needed structured, predictable data, not a wall of text. I did this by defining a responseSchema and setting the response type to JSON, which is essentially handing the AI a strict template to fill out: dogName, feeding, meds, walkRoutine, and so on. That made the output reliable and readable by the front end.

Working with the schema taught me the UI and the schema have to stay in sync. While testing, I tried to render a "vet info" field on the card and the data kept coming back empty. The schema was actually asking the AI for an emergencyContact string, not vetInfo, so the front end was reading a field that didn't exist. The lesson: the schema is the source of truth. If you want a new field on the front end, you define it in the backend schema first.

Structured output is also a guardrail against the AI inventing things. I added an instruction to the prompt: if a detail is missing, write a friendly placeholder like "Not specified, ask the owner." So if an owner forgets to mention medication, the app doesn't hallucinate a fake dosage to fill the field, it honestly flags the gap. The data stays trustworthy, which matters a lot when someone is using this to actually care for a dog.

The hidden tension between retries and serverless timeouts. I hadn't built much on top of an external AI API before, so the unexpected caught me off guard. During testing, calls would occasionally fail with a 503 Service Unavailable. That wasn't a bug in my app, it meant Gemini's model was temporarily overloaded on their end. So I added a retry loop with exponential backoff: if a call fails, wait and try again, waiting a little longer each time so I'm not spamming the API. I bumped max attempts from 4 to 6, figuring the model was just busy and I should wait it out.

That "fix" surfaced something worse: 502 Bad Gateway. The 502 told me the failure had moved to my side, the Netlify function was being killed before it could return a clean response. I dug into the function logs and found it: Status: timeout at 30 seconds. That was the lightbulb. My growing backoff (500ms doubling up toward 8s) across 6 attempts was pushing the function past the serverless 30-second execution limit. Netlify doesn't wait around for the 6th try, it kills the process. My attempt to make the app more resilient was causing a crash.

The real fix was designing within a time budget. You can't write a retry loop without knowing the execution limit of the environment running it. I dropped max attempts back to 4 and capped the backoff at 1.5 seconds so the wait can't grow unchecked. The goal isn't to never fail, it's to fail gracefully. If Gemini is honestly unavailable, the function stays within budget, catches the error, and returns a friendly message instead of dying silently in the background.

Prize Categories

This project is submitted for Best Use of Google AI.

Gemini is the engine that makes Denver's Care Card work. Rather than using it as a novelty, I used it to solve the core product problem: turning a dog owner's quick, unstructured note into reliable, structured data. Two Gemini features made that possible. First, responseSchema with JSON output forces the model to return the same seven fields every time, so the React front end can render a consistent care card instead of parsing free-form text. Second, careful prompting keeps the output honest: when a detail is missing, the model returns a clear placeholder like "Not specified, ask the owner" rather than inventing a fake one, which matters when the result is meant to help someone actually care for a dog.

Handing your dog to someone else is a small act of trust. This little tool tries to make that handoff a bit calmer, and a bit clearer.

Top comments (0)