Bundled or Your Own Backend — Mobile Boilerplate — Mobile App Boilerplate Docs

The app is backend-agnostic by design. You have two clean paths.

apps/api is a Hono and Drizzle backend on SQLite, with auth, /me , devices, uploads, payments, and webhooks already implemented. It runs through tsx , so there is no build step, and it ships a Dockerfile for deployment.

Outgrowing a single SQLite file? docs/swap-to-postgres.md in the repository documents the change.

Endpoints it already serves: /healthz , /auth/* (sign-up, sign-in, refresh, sign-out, forgot and reset password, Apple, Google), /me (read, update, delete), /devices , /uploads , /payments , /webhooks .

Already running Django, Rails, FastAPI, or Node? Then:

The contract is small. At minimum you need sign-up, sign-in, token refresh, and GET /me . Devices, uploads, and payments are only needed if you use those features.

Skip backend auth entirely

Use a hosted provider and your backend only has to verify their tokens. See Choosing an Auth Provider for what that actually involves.

Every request goes through apiFetch in packages/api-client , which adds the auth header, validates the response against a zod schema, and turns failures into a normalised ApiError . TanStack Query sits on top for caching, retries, and refetching.

Swapping backends never touches your screens. It changes one env var and, if your shapes differ, the schemas in packages/api-client/src/schemas .

Mutations go through TanStack Query's onlineManager , bound to NetInfo. When the device goes offline, writes pause instead of failing, and flush when the connection returns. docs/recipes/offline-queue.md covers the details.

Try this one on a device, not in a browser

NetInfo's web build reports the connection as online regardless of what the browser is doing, so on the web target the banner never changes and a queued write just fails. Use airplane mode on a real device or a simulator to see the queue actually hold and flush.

Recommended articles