Open-source project
animavita/animavita avatar
animavita/animavita

Animavita is a rebuild of a pet adoption app around a single owner-to-adopter loop

Trigger life-saving alerts, register animals for adoption and find the closest pet friend to adopt :dog:

734 stars318 forksTypeScriptGPL-2.0

At a glance

What is it?
Animavita is a React Native and NestJS application for registering animals for adoption and finding the closest one, and the current v2 branch is a rebuild that deliberately narrows the scope to one loop: post a pet, find one nearby, request it, talk it over, agree. The project publishes its domain rules in CONTEXT.md and its trade-offs in architecture decision records, and the roadmap status is computed from issues rather than typed by hand.
Who is it for?
Adopt the Animavita codebase if you are looking for a worked example of a React Native and NestJS monorepo with a documented domain model, an ADR habit and an issue-driven development workflow. Skip it if you want a deployed adoption service, since the project status badge reads WIP, initial development is in progress and there has not yet been a stable usable release for the public.
Can I use it commercially?
Yes, with conditions. GPL-2.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 90 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The purpose is to centralise adoption information, not to replace Facebook

Animavita is described as a minimal, clean and beautiful mobile app to help people find the closest pet friend to adopt, rescue lives and change the world.

The name is a combination of two Latin words, Animal and Vitae, which means respectively Animal and Life, and the project describes it as an idea to create or animate something that was born a while ago, when the author realised that Facebook is used to publicise adoption and also to ask for help for animals that live on the streets.

The scope statement is unusually precise. Animavita's purpose is not to change how people use Facebook to the activities described, but to centralize the helpful information in a single application.

Then the part that explains the product design. Anyone can make an adoption request, but it doesn't mean that the pet will be automatically adopted. It means that the person interested can talk to the person who registered the pet, allowing both sides to have a conversation, and the user to research and decide his favourite pet.

So the app is not a listing with a claim button. It is a two-sided conversation tool where the outcome is deliberately not guaranteed, and the design has to make reaching the other person easy and deciding harder than clicking.

The repository is TypeScript, GPL-2.0 licensed, with the default branch v2, a Discord server, a live site at animavita.pet, and a project status badge that reads WIP, meaning initial development is in progress but there has not yet been a stable, usable release suitable for the public.

v2 is a rebuild that narrows the feature list on purpose

The roadmap section is the clearest statement of what the project is doing and why.

v2 is a rebuild: a simpler Animavita with a cleaner UI and a codebase that makes new features cheap to add. The goal is to get the whole owner-to-adopter loop working end to end, and that loop is enumerated as post a pet, find one nearby, request it, talk it over, agree. The stated aim is also to find out whether the decisions in discussion #120 hold up in practice.

That last clause is the interesting one. The rebuild is not only a technical refresh, it is a test of a set of product decisions, using the code as the instrument. The earlier features are explicitly deferred. Complex features like in-app chat and push notifications come later.

The status tracking is worth noticing because it is unusual. The milestone is the single source of truth for v2 status. It used to be a checklist in the README, which drifted out of step with both the issues and the code, so status now lives in one place only, where it's computed rather than typed.

That is a small but real engineering decision. A hand-maintained checklist in a README is a snapshot that goes stale, and the failure mode is a reader trusting it. Computing the status from the issue tracker removes the second source of truth entirely, at the cost of the README no longer being readable offline.

The contributor-facing labels continue the same idea. There is a good first issue label for small, self-contained work, and a ready-for-agent label for issues that are fully specified with acceptance criteria and scope boundaries written down, described as nothing left to guess at. Some issues declare which others must land first, and GitHub shows those as blocking relationships, so if an issue looks blocked, pick a different one, and the frontier is whatever has no open blockers.

The domain is documented, and it is out of step with the code on purpose

The section on how the app works points at two documents and gives an instruction about them that is the most useful sentence in the README for a new contributor.

The domain, what a Pet, an Adopter, an Adoption Request and a Wishlist actually are and the rules that govern them, is documented in CONTEXT.md. The decisions behind those rules, and the trade-offs they cost, are in docs/adr/.

And then: read both before working on adoption logic. Several decisions there deliberately differ from what the current code does.

That is unusual and it is a deliberate design, not an oversight. In most projects, the documentation describes the code, and when they disagree the code wins. Here, the specification is ahead of the implementation, because the v2 rebuild is a redesign and the domain model is being decided before the code catches up. A contributor who reads only the code would implement the old behaviour and think they were finished.

The ADR directory is the mechanism for recording why, and it is the part of the structure that tells you how the project is run. Architecture decision records exist to capture a decision together with the alternatives considered and the cost accepted, so a later reader can see the reasoning rather than infer it.

The domain model itself is small and worth naming, because the four nouns are the product. A Pet is something registered by an owner. An Adopter is the person who wants one. An Adoption Request is the conversation between them. A Wishlist is what an adopter is looking for. Everything else in the app exists to serve those four.

A pnpm workspace with a mobile app and a NestJS backend

The repository layout is a monorepo with an apps/ directory, a shared/ directory and a workspace configuration, and the root package.json shows how the two halves are driven.

There are filter-based scripts for each side. `pnpm --filter backend` and `pnpm --filter mobile` run the relevant commands, and the composite scripts chain them: `test` runs `pnpm backend test && pnpm mobile test`, `lint` does the same for lint, `check-types` does the same for check-types, and `verify` runs check-types, then lint, then test. There is also a `clean` script that removes every node_modules directory with a find and a prune.

That verify script is the one to point a CI pipeline at, since it is the whole check in one command.

The stack in the topic tags is React Native with Expo for the mobile side, NestJS and Node for the backend, with hooks and JavaScript alongside. The mobile app is the part a user sees and the backend is the API, and the pnpm workspace is what keeps them in one repository without coupling their dependency trees.

The root package also shows some pragmatism. There is a patchedDependencies entry for native-base at 3.4.25 pointing into a patches/ directory, with allowNonAppliedPatches enabled so the build does not fail when a patch does not apply. There is an override forcing the AWS SDK client for S3 to a specific version. And there is an onlyBuiltDependencies list with argon2 and mongodb-memory-server, which is the pnpm mechanism for allowing packages that run install scripts to do so.

The package manager is pnpm at 9.15.9, the dev dependency is nx at 15.9.2, and the author is Gabriel Belgamo. The licence in the package file is ISC while the repository licence file is GPL-2.0, which is a discrepancy worth flagging before you copy the code.

Local development runs MongoDB and a local S3 in containers

The docker-compose file is the clearest statement of the backend's dependencies, and it is a small and conventional stack.

The backend service builds from apps/backend/Dockerfile with a target of development, mounts the backend source at /app, and depends on both mongodb and localstack. It publishes port 3000 and runs pnpm run start:dev. Its environment is where the configuration surface shows: a MongoDB URI, a JWT access secret with a 24h access token expiration, a JWT refresh secret with a 14d refresh token expiration, an AWS region, test access keys, an S3 bucket name and an AWS endpoint pointing at localstack.

Two details in that list are worth pausing on. The separate access and refresh secrets with different expirations are a deliberate design, so a leaked access token is short-lived. And the AWS_ENDPOINT pointing at localstack is what lets the app develop against S3 without touching a real bucket.

MongoDB runs as image mongo:7.0.0 with port 27017 published, a root username and password of mongo, an initial database of animavita, and a named volume for the data. LocalStack runs as localstack/localstack:3.0 with port 4566 published, SERVICES set to s3, DEBUG on, persistence on, a named volume, and and a mount of a localstack-init.sh script into the ready.d directory.

That last mount is the piece that makes the local environment usable rather than merely runnable. Without it the app would start against an S3 endpoint with no bucket in it.

Where this sits against a general purpose adoption platform

The comparison that matters is with the existing pet adoption platforms, and the project makes the argument itself in the Why section.

Facebook is the incumbent, and it is used to publicise adoption and to ask for help for animals on the streets. The argument is not that Facebook is bad for this, it is that the information is scattered and the mechanism for a serious adopter is a comment on a post. Animavita's claim is about centralization, and the conversation-first design is what follows from taking that seriously.

Against a dedicated adoption platform, the difference is the explicitness of the owner-to-adopter conversation. A listing site with an apply button optimises for throughput of applications, and it treats the adoption as a transaction to be closed by whoever is quickest. Animavita's model, where a request means talking to the person who registered the pet, optimises for the pet being right for the person, and accepts a lower request volume.

That is a real product decision with real costs. It will always be slower to close an adoption than a one-click flow, and it needs a critical mass of registered pets before a nearby adopter has anything to look at, which is the hard part of any two-sided local marketplace.

For an engineer reading the codebase, the more transferable thing is the process. A domain model written down before the code, a set of ADRs explaining the trade-offs, a status page computed from the issue tracker instead of typed by hand, and issue labels that tell a new contributor what is unblocked and what a task is allowed to include. That is a small project with unusually disciplined documentation, and the documentation is what you can learn from whether or not you adopt pets.

Editorial conclusion

Adopt the Animavita codebase if you are looking for a worked example of a React Native and NestJS monorepo with a documented domain model, an ADR habit and an issue-driven development workflow. Skip it if you want a deployed adoption service, since the project status badge reads WIP, initial development is in progress and there has not yet been a stable usable release for the public. Verify first which branch you are on, because the default branch is v2 and the v2 rebuild has different behaviour from what the code currently does, and read CONTEXT.md and the ADRs before touching any adoption logic, as the documentation says several decisions there deliberately differ from the current implementation.

Frequently asked questions

What is Animavita and what is it built with?

It is a mobile app for finding the closest pet friend to adopt, registering animals for adoption and triggering life-saving alerts. The stack is React Native with Expo on the mobile side, NestJS and Node on the backend, TypeScript throughout, in a pnpm workspace with nx, and the project is GPL-2.0 licensed.

What is the v2 rebuild trying to achieve?

A simpler Animavita with a cleaner UI and a codebase that makes new features cheap to add, by getting the whole owner-to-adopter loop working end to end: post a pet, find one nearby, request it, talk it over, agree. The goal is also to test whether the decisions in discussion #120 hold up in practice, and complex features like in-app chat and push notifications come later.

How do I set up the Animavita backend locally?

The docker-compose file brings up the backend, MongoDB 7.0.0 on port 27017 and LocalStack 3.0 on port 4566, with the backend on port 3000 and a mounted LocalStack init script. The root package.json provides pnpm filter scripts for backend and mobile, and a verify script that runs check-types, lint and test in sequence.

Official sources

  1. animavita/animavita on GitHub
  2. Issues
  3. License: GPL-2.0
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/animavita-animavita.svg)](https://hysenlabs.com/projects/animavita-animavita)

Was this useful?