A mobile app is only as good as the API behind it. Slow screens, confusing errors, broken older versions after a backend release and battery-draining sync are usually API problems, not app problems. And unlike a website, a mobile app cannot be updated instantly for everyone: users keep old versions for months.
That changes how the backend should be designed. This post covers the practices I use for mobile backends, such as the .NET Core API behind the NeonTask technician app and web app, and the Next.js backend behind Pro Clean+.
Design for screens and tasks, not tables
The most common mistake is an API that mirrors the database: one endpoint per table, leaving the app to assemble each screen from many requests. On a mobile network, every request costs time.
Instead, design around what the app needs:
- A screen that shows a booking with its service, address, staff and status should get it in one request.
- A list screen should return exactly the fields the list shows, not every column of every record.
- Actions should be explicit: "confirm booking", "check in to job", "complete visit", rather than generic updates of a status field.
Explicit actions also make business rules easier to enforce on the server: who can do what, in which state, and what happens as a result.
Versioning: old apps never disappear
When you release a new web app, everyone gets it at once. When you release a new mobile app, some users update immediately, some weeks later and some never. Your backend must serve them all.
Rules that keep this manageable:
- Never remove or rename a field that a released app version uses. Add new fields instead.
- Never change the meaning of an existing field.
- Make new request fields optional, with sensible defaults for older apps that do not send them.
- Version the API when a breaking change is unavoidable, and keep the old version running until usage drops.
- Send the app version with every request, so you know which versions are still in use.
- Support a minimum version check: the API can tell very old apps to update, with a friendly screen in the app. Use it sparingly, but have it ready from the first release.
Test new backend releases against the current and previous app versions on staging. See why you need a staging environment.
Pagination and filtering
Every list endpoint should be paginated from day one, even if lists are short at launch. Cursor-based pagination, using "give me items after this one", behaves better than page numbers when new items arrive while the user is scrolling.
Filtering and sorting should happen on the server, with indexes in the database to support them. See how far a single server can take your product for why this matters for performance.
Errors the app can act on
An error response should tell the app what happened in a way it can handle:
- A stable error code the app can switch on, such as "slot_unavailable" or "quote_expired".
- A human-readable message, ideally in the user's language, for display.
- Field-level details for validation errors, so the app can highlight the right input.
- Correct HTTP status codes: client errors as 4xx, server errors as 5xx.
The app should never have to parse an English sentence to decide what to do. And for bilingual apps, messages should respect the user's language. See bilingual Arabic and English apps.
Authentication
For most business apps:
- Short-lived access tokens with longer-lived refresh tokens, stored securely on the device.
- Refresh tokens that can be revoked, so a lost phone or a departing staff member can be cut off.
- Permissions checked on the server for every request, based on the user's role and the record's owner. Never rely on the app hiding buttons.
- Rate limits on login, OTP and password reset endpoints.
More on this in web app security basics.
Uploads
Photos are central to many apps: technicians documenting jobs, customers sending photos for a quote, as in Pro Clean+'s photo-based quotes. Uploading them well:
- Resize and compress on the device before upload. Full-resolution photos waste data and time.
- Upload directly to storage with a pre-signed URL where possible, rather than through the API server.
- Make uploads resumable or retryable, and decouple them from the main action, so a failed photo does not lose the whole job report.
- Process asynchronously: thumbnails and checks in a background job.
Slow and unreliable networks
Design for the network your users actually have:
- Keep responses small. Compress them.
- Make write actions idempotent: if the app retries a request after a timeout, the server should not create a duplicate booking. An idempotency key sent by the app solves this.
- Return the updated record after an action, so the app does not need a second request.
- For apps that must work offline, design sync explicitly: what is stored locally, how changes are queued and how conflicts are resolved. See building apps that keep working on unreliable connections.
Timestamps and time zones
Store and send timestamps in UTC with an explicit offset format, and convert to local time in the app. Bookings and appointments also need the business's time zone stored with them, so a slot at 10:00 means 10:00 where the service happens, even if the user's phone is set differently. This matters as soon as you serve customers in more than one country.
Documentation and contracts
Describe the API in a machine-readable format such as OpenAPI. It serves as documentation, lets you generate typed clients for the app, and makes changes visible in code review. A typed client turns many backend changes that would break the app into compile errors instead of crashes.
Observability
Log every request with its endpoint, status, duration, app version and a request ID that the app can show in error screens. When a user reports a problem, that ID leads straight to what happened. Track error rates and slow endpoints, and alert on them. See monitoring and alerts for a small product.
One API, several clients
Most products end up with several clients: the customer app, a staff app, a web admin. NeonTask has a web app for dispatchers and admins and a technician app, both served by one API. Share the core business logic in one backend, and shape endpoints for each client where their needs differ, rather than duplicating rules in each app.
Common mistakes
- Endpoints that mirror tables, forcing many requests per screen.
- Breaking changes deployed without considering old app versions.
- No pagination.
- Error messages as free text only.
- Permissions enforced only in the app.
- Uploading full-size photos through the API server.
- No request IDs, so bugs cannot be traced.
Summary
Design the API around screens and actions, treat every released app version as a client you must support, keep responses small, make retries safe, and document the contract. These habits cost little at the start and save a great deal once the app is in users' hands.
If you are planning a mobile app or reworking its backend, see my work and get in touch.