Skip to main content
Product engineering & technology · 03

Backend & API engineering

Product problems that look like frontend bugs are often backend contracts that were never written down. We design and build the services underneath your app or website: API contracts, authentication, business rules, data models, integrations and the observability that tells you when one of them is failing.

  • REST & JSON contracts
  • OAuth & session auth
  • PostgreSQL · Redis
  • Webhooks & integrations

BUILD

Product engineering & technology

The applications, interfaces, services and infrastructure your business actually runs on — mobile, web, backend, cloud and the automation around them, built by the same people who will later measure and advertise them.

What backend work actually is

A backend is the place where your business rules become enforceable. The interface can ask for anything; the service decides what is allowed, what it costs, who may see it and what happens when a step half-succeeds. When that logic is scattered across an Android app, a web client and a few cloud functions, every change has to be made in each of them and the versions drift apart. We put it in one place and give the clients a contract to call.

An API is a contract before it is an implementation. The shape of a resource, the meaning of each field, the errors a caller must handle and the guarantees about repeated calls are decisions that outlive the framework they were written in. We write those decisions down first, in a schema a client developer can read, then build against them. Changing an implementation is cheap. Changing a contract that mobile clients already ship against is not.

Most of the difficulty is not in the happy path. It is in the payment webhook that arrives twice, the third-party call that hangs instead of failing, the migration that locks a table at the busiest hour of the day, and the query that was fine until one account's history grew large. We design for those cases deliberately, because they are the ones that produce refunds, duplicate orders and support tickets rather than tidy error pages.

The symptoms that bring people here

Requests for backend work rarely arrive phrased as backend problems. They arrive as a symptom in the product, in the support inbox or on an invoice.

The same rule is implemented three times
Pricing, eligibility or discount logic lives in the Android app, the website and a background script, each written at a different time by a different person. Every one of them was correct on the day it shipped. Now a change to the rule needs several releases, one of which waits for store review, and the versions disagree until the last of them lands.
Customers are charged twice
A payment succeeds, the response is lost on a flaky mobile connection, the client retries, and the charge lands again. Underneath, the endpoint has no idempotency key and no record of which request it already processed. The same pattern produces duplicate orders, double-sent messages and support conversations nobody can settle from the logs.
The app slows down as data grows
A list screen that loaded instantly in testing now takes seconds for your largest accounts. The endpoint returns every row, the ORM issues a query per item inside a loop, and the column being filtered has no index. Nothing is broken in a way that raises an alert, so it degrades quietly until a customer complains.
Nobody can say why a request failed
An integration stops working and the investigation begins with screenshots. There are no request identifiers, errors come back as a generic five hundred with an HTML body, logs are unstructured, and the third-party call that actually timed out is invisible. The failure is reproducible only by the customer, which makes it expensive to fix.
What we deliver

What we build

Scope varies with the product, but the same concerns turn up in any backend that has to keep running after launch.

01

API design and contracts

We define the interface before we write the service: resources, field meanings, status codes, error shapes and pagination rules, expressed in a schema rather than in a chat thread. Client and server work against the same document, and a breaking change becomes a visible decision instead of an accident.

  • OpenAPI schemas kept in the repository alongside the code
  • Consistent resource naming, filtering and sorting conventions
  • Cursor or keyset pagination for lists that will grow
  • A single error envelope with machine-readable codes
  • An explicit versioning strategy and a deprecation path
  • Typed clients generated for web and mobile where it helps
02

Authentication and authorisation

Who you are and what you may do are separate problems, and the second is the one usually got wrong. We implement session or token authentication suited to the client, then enforce permissions at the data access layer, so a single forgotten check in a handler is not the only thing keeping one tenant's records away from another.

  • Email, phone-OTP and social sign-in flows
  • Access and refresh token handling, with rotation and revocation
  • Role and attribute-based permission checks close to the data
  • Multi-tenant isolation enforced by query scope, not by convention
  • Password reset, session invalidation and device sign-out
  • Service-to-service credentials kept out of the codebase
03

Domain logic and data modelling

Schema decisions are the ones you live with longest. We model entities, relationships and state transitions against how the business actually behaves, keep the rules in the service layer rather than in triggers or client code, and write migrations that can run on a live database without a maintenance window.

  • Relational schema design with real constraints and foreign keys
  • State machines for orders, subscriptions and approvals
  • Reversible, incrementally applied migrations
  • Indexes chosen from query plans rather than from guesswork
  • Soft deletion and history where the business needs an audit trail
  • Seed and fixture data for realistic local testing
04

Integrations and webhooks

Payments, messaging, CRM and logistics systems fail in ways your own code does not. We treat each integration as an untrusted boundary: verified webhook signatures, replay-safe handlers, timeouts on every outbound call, and a stored record of what the third party sent, so a dispute can be settled from data.

  • Payment gateway integration, including refunds and failure states
  • Signature verification and replay protection on inbound webhooks
  • Outbound webhooks for your own customers, with retries and a delivery log
  • Email, SMS and WhatsApp Business messaging providers
  • CRM and spreadsheet synchronisation with defined conflict rules
  • Sandbox and production credentials kept separate
05

Background jobs, caching and rate limits

Anything slow, repeated or externally dependent belongs off the request path. We move it into queues with retry policies and dead-letter handling, cache what is expensive to compute and cheap to invalidate, and apply rate limits per client so one badly behaved caller cannot degrade the service for everyone.

  • Queued jobs for exports, notifications, imports and reconciliation
  • Scheduled tasks with locking so they do not run twice
  • Exponential backoff, retry caps and dead-letter inspection
  • Response and query caching with explicit invalidation rules
  • Per-key and per-IP rate limiting with honest 429 responses
  • Idempotency keys on every endpoint that moves money or state
06

Observability and operational readiness

A service you cannot see is a service you cannot support. Structured logs carry a request identifier through every hop, errors are grouped rather than mailed one at a time, and dashboards show latency and failure rates for the endpoints that matter commercially, not for all of them equally.

  • Structured JSON logging with correlation identifiers
  • Error tracking with grouping, release tagging and ownership
  • Latency and error-rate views for the critical endpoints
  • Health and readiness checks used by the deployment itself
  • Alerting tied to symptoms customers feel, not to raw CPU graphs
  • Runbook notes for the failure modes we already know about
Technical approach

How the work runs

Backends fail slowly, so we front-load the decisions that are expensive to reverse and leave the reversible ones for later.

  1. 01

    Read what is already there

    We start with whatever exists: the current API, the database schema, the mobile app that calls it, the support tickets. Reading real request logs tells us more than a requirements document, because it shows which endpoints are actually used and which fail. The output is a short written account of the current shape and the risks in it.

  2. 02

    Agree the contract and the data model

    Before implementation, we write the schema: resources, fields, error codes, permission rules and the states each entity can be in. Your client developers review it while it is still cheap to change. Disagreements about what a field means surface here, in a document, rather than later in an integration call with a release date attached.

  3. 03

    Build a thin slice end to end

    The first working increment goes all the way through: authentication, one real endpoint, the database, a deployment, logs and an error report. It proves the environments, the pipeline and the contract before the volume of code makes any of them expensive to change. Client teams get something callable early instead of waiting for a complete backend.

  4. 04

    Harden the edges

    Once the core behaves, we go after the parts that only fail in production: idempotency on state-changing endpoints, timeouts and retries on every outbound call, rate limits, pagination on lists that will grow, and indexes chosen from actual query plans. This is unglamorous work, and it is where production incidents get prevented rather than diagnosed.

  5. 05

    Hand over so someone else can run it

    A backend nobody else understands is a liability. You get the schema, environment setup that works from a clean machine, migration and deployment instructions, a map of every integration and where its credentials live, and notes on the failure modes we know about. Whether we stay on afterwards should be a choice, not a dependency.

The path of a single request

The decisions on this page are easier to judge if you follow one request from the client to the response. Each stage below is a place where an API is commonly got wrong.

  1. 01

    Edge and routing

    The request arrives at a load balancer or edge function, terminates TLS, and is matched to a route. Malformed and oversized payloads are rejected here, along with traffic from clients that have already exceeded their rate limit.

  2. 02

    Authentication

    The token or session is verified before any handler code runs: signature, expiry, audience and revocation. The result is an identity attached to the request, not a boolean. Everything downstream reads that identity rather than trusting a value sent by the client.

  3. 03

    Validation

    Body, query and path parameters are parsed into typed values against a schema. Anything unexpected is refused with a field-level error a client can display. Validation happens once, at the boundary, so the rest of the code can assume its inputs are sane.

  4. 04

    Business logic and authorisation

    The service layer decides whether this identity may perform this action on this record, applies the domain rules, and checks the idempotency key if the operation changes state. This is the only layer that knows what the business means; handlers and repositories do not.

  5. 05

    Data access

    Reads and writes go through a repository with the tenant scope already applied. Multi-step changes run inside a transaction, so a partial failure leaves nothing half-written. Calls out to payment or messaging providers are queued rather than made inline wherever they can be.

  6. 06

    Response and observability

    The response carries the agreed shape, an accurate status code and, on failure, a stable error code the client can branch on. The same request identifier appears in the log line, the error report and the latency metric, so one failure can be traced end to end.

Things that decide whether an API survives contact with production

None of these are visible in a demo. All of them decide what the second year of running the service looks like.

  • Every endpoint that moves money or changes state accepts an idempotency key, and a repeated call returns the original result instead of doing the work again.
  • Lists are paginated by cursor from the start, because offset pagination breaks precisely when a dataset gets large enough to matter.
  • Errors share one envelope with a stable machine-readable code, so clients branch on the code rather than on English text that will be reworded.
  • A versioning strategy exists before the first client ships, together with an agreed way to deprecate an old version and evidence for when it is safe.
  • Every outbound call has a timeout, a retry policy with backoff, and a defined behaviour for when the retries run out.
  • Query plans are checked for N+1 access patterns and missing indexes on the endpoints that carry real traffic, not on all of them equally.
  • Migrations are reversible, applied by the pipeline rather than by hand, and rehearsed against a copy of production-shaped data.
  • Secrets live in a managed secret store, never in the repository, and can be rotated without a code change or a redeploy of the client.
  • Rate limits are set per client and return an honest 429 with a retry hint, instead of silently degrading for everyone.
  • Changes to sensitive records are written to an audit trail that records who did what, when, and from where.

On scale claims

We will not tell you that an API is built to handle millions of users, because that sentence means nothing without a workload attached. We design for the load you have and the next order of magnitude above it, using your own numbers wherever they exist. Then we tell you which part would break first, what the symptom would look like, and roughly what it would cost to move that ceiling when you actually need to.

Technology

Technology we work in

These are tools we use, named descriptively so you can see what the work involves. Naming a product implies no partnership, sponsorship, certification or endorsement from its owner, and every trademark belongs to its respective owner. The stack for your project is chosen after we understand the workload, not before.

Languages and runtimes
  • TypeScript
  • Node.js
  • NestJS
  • Express
  • Python
  • FastAPI
  • Kotlin
Data and storage
  • PostgreSQL
  • MySQL
  • Redis
  • Prisma
  • Drizzle ORM
  • Firestore
  • Amazon S3
  • Google Cloud Storage
Interfaces and integrations
  • REST
  • OpenAPI
  • GraphQL
  • JSON Web Tokens
  • OAuth 2.0
  • Stripe
  • Razorpay
  • Twilio
Delivery and operations
  • Docker
  • GitHub Actions
  • Google Cloud Run
  • AWS Lambda
  • BullMQ
  • OpenTelemetry
  • Sentry
  • Grafana
Outcomes

What you are left with

  • A documented API your own developers can build against without asking us
  • Business rules in one place, changed once rather than in every client
  • Retries, repeats and duplicate webhooks that do not corrupt your data
  • Failures you can trace from a customer complaint to a log line
  • Code, schemas and infrastructure in repositories and accounts you own
Questions

What people ask before starting

01Who owns the code, the repository and the credentials?

You do. Code lives in a repository under your organisation from the first commit, with full history. Cloud projects, databases, payment dashboards and third-party accounts are created in your name, and we work as invited collaborators. If the engagement ends, access is revoked and nothing stops working. We do not hold anything hostage as a retention mechanism, and there is no proprietary layer you have to keep paying for.

02What actually drives the cost of a backend project?

Integrations, mostly. Each external system adds its own failure modes, sandbox quirks and reconciliation work, and payment providers add the most. After that: how many distinct roles need different permissions, whether data has to be migrated out of a live system, and how precisely the business rules are known before we start. Vague requirements are the most expensive input, because they get discovered during implementation rather than before it.

03Can you work with the backend we already have?

Usually, yes. We read what exists, document the contracts it already exposes, and add new capability alongside it rather than replacing everything at once. Endpoints are moved over one at a time behind the same routes, so clients do not have to change on our schedule. If a rewrite is genuinely cheaper than maintaining what is there, we will say so and show the reasoning rather than quietly starting one.

04How do you change an API that shipped mobile apps depend on?

Carefully, because old app versions live on devices you do not control. Additive changes go out unversioned: new optional fields, new endpoints. Anything that removes a field, changes its meaning or tightens validation goes behind a new version, with the previous one kept alive until traffic from old clients has fallen away. We track which versions are still being called, so retiring one is a decision made from data.

05What happens if the service fails in production?

Something will fail eventually. What we build in advance is the ability to find out quickly: error tracking that groups failures and names the release, structured logs with request identifiers, and a deployment that can be rolled back. What we do not sell is an uptime guarantee. Support after launch is a written arrangement with agreed response expectations, and we would rather set one you can hold us to.

06What will you not agree to?

We will not store raw card details, accept production credentials over chat, or push a schema change to a live database without a reversible migration and a backup. We will not promise a performance figure before we have measured anything. And we will not build an integration against a third-party system we have no sandbox access to and no documentation for, because that is a guess billed as engineering.

Next step

Talk about your backend and integrations

Send the API documentation you have, or a description of what your app needs to do. We will read it and come back with the contract questions that matter before quoting anything.

Directcontact@mnfinfotech.com