Laravel
Maintainable backends: structure, Eloquent, queues, authorization and SaaS patterns.
- Application structure
- Eloquent
- Queues
- Testing
APIs are the contract between systems. I approach API development around predictable behavior, clear ownership, security, validation and long-term maintainability rather than simply exposing database operations over HTTP.
API design · Error handling · Idempotency · Webhooks
How I think about it
A good API should make expected behavior obvious and failure behavior predictable. Clients should not need to guess how authentication, validation, pagination or errors work.
Key experience
Scope
Deep dive
Endpoints describe resources and their relationships — /organizations/{id}/members — and HTTP methods describe the action. Status codes carry meaning clients can rely on: 201 for creation, 204 for no content, 404 when a resource doesn't exist or isn't visible to the caller, 409 for conflicts, 422 for validation failures.
Predictability beats cleverness: the same naming, date formats, ID formats and casing on every endpoint, so a developer who has used one endpoint can guess how the next one works.
Client
API layer
auth, validation
Service
business rules
Data / external provider
scoped, mapped
Validation happens at the boundary, against an explicit schema: required fields, types, formats, allowed values, lengths. Unknown fields are rejected or ignored deliberately, never silently stored. The error response says exactly which field failed and why, in a structure the client can map back onto its form.
Responses are shaped for clients, not copied from the database: internal columns stay internal, and renaming a database field doesn't change the contract. Envelopes such as data and meta are useful when there's real metadata — pagination, totals, warnings — and noise when there isn't. Whichever choice is made, it's the same everywhere.
Clients should be able to handle every error with one piece of code: the same structure for validation, authentication, authorization, not-found, conflict and server errors, with a stable machine-readable code and a human-readable message.
Internal errors return a generic message and a request ID. The stack trace goes to the logs, never to the client.
{
"error": {
"code": "validation_failed",
"message": "The request contains invalid fields.",
"fields": {
"email": ["Enter a valid email address."],
"role": ["Role must be one of: admin, member."]
},
"request_id": "req_8f2c1a"
}
} The mechanism follows the client. First-party web apps on the same domain often do best with secure session cookies. Mobile apps and SPAs on other domains use short-lived access tokens with refresh. Server-to-server integrations use API keys or client credentials scoped to what the integration needs and rotatable without downtime. OAuth matters when users grant a third party access to their data — not as a default for every API.
Versioning is a tool for breaking changes, not a ritual. Most evolution can be additive — new fields, new endpoints, new optional parameters — and stays backward compatible. When a breaking change is unavoidable for external clients, a new version with a deprecation period is kinder than a silent change. For an API used only by your own frontend and deployed together with it, versioning usually adds cost without benefit.
Offset pagination (?page=3) is simple and supports jumping to a page, but it slows down on large tables and can skip or repeat items when rows are inserted while a client pages through. Cursor pagination (?after=…) is stable and efficient for feeds, syncs and large datasets, at the cost of random access.
I choose per endpoint: offsets for admin tables, cursors for anything large or continuously changing.
Filters and sort fields are an allow-list, validated like any other input and never passed straight into a query. Each one is a performance commitment: an endpoint that allows sorting by any column across millions of rows needs indexes to match, or it becomes the slowest query in the system.
Networks fail after the server has done the work but before the client sees the response, so clients retry. For operations with side effects — payments, orders, invitations — a retry must not repeat the effect. The client sends an idempotency key; the server stores it with the result and returns the original result for a repeated key instead of performing the operation again.
If retrying a request can charge a customer twice, the API isn't finished.
Webhook handlers should assume events can be delayed, retried or delivered more than once. Processing should therefore be idempotent wherever duplicate execution could cause side effects.
In practice: verify the signature, store the event, return a 2xx quickly, and process it in a background job keyed on the event ID. Events can also arrive out of order, so handlers check the current state — or fetch the latest object from the provider — instead of trusting the order of arrival.
Provider event
Verify signature
Store event
keyed by event ID
Acknowledge
2xx, fast
Process async
idempotent
Every external provider will eventually be slow, rate-limited or down. Calls get explicit timeouts, retries with backoff only for safe operations, respect for rate-limit headers, and caching for data that rarely changes. The provider's data model is mapped to yours at the edge, so their naming and quirks don't spread through the codebase — and switching providers stays possible.
An API is only as usable as its documentation: each endpoint's purpose, authentication, parameters, responses and errors, with realistic examples. For public or partner APIs, an OpenAPI description generated from — or validated against — the code keeps documentation and behaviour aligned. Hand-written docs that drift are worse than none, because clients trust them.
Authentication on every non-public endpoint, authorization on every resource, validation on every input. Rate limits protect login, password reset and expensive endpoints. Responses never include more data than the client needs — over-exposed fields are a common source of leaks. Logs record request IDs, routes and outcomes, not passwords, tokens or full payloads containing personal data.
API tests exercise the contract the way clients do: real HTTP requests against the application with a test database. The happy path is the smallest part. Validation failures, missing authentication, the wrong role, another tenant's resource, conflicts and retried requests are where the bugs that matter live. Response shapes are asserted, so an accidental contract change fails a test instead of a client.
In practice
Real products
APIs sit at the centre of the SaaS platforms and business systems I've built — between frontends, backends and third-party services.
Accessibility Technology / SaaS
API-driven SaaS: a React frontend on Node.js APIs, with Stripe payments and AWS Bedrock as integrated services.
SaaS / Portfolio Builder
A Laravel API serving a React frontend, plus a Python microservice — APIs between services, not only between client and server.
Enterprise SaaS
Platform built around REST APIs — I led the architecture, backend, database design and the frontend integration consuming them.
School Management SaaS / EdTech
School management SaaS with a Laravel backend serving the platform through APIs, alongside payments and role-based access.
Full-stack and lead developer roles
Integrations, APIs and internal business systems built with Laravel and Node.js.
Maintainable backends: structure, Eloquent, queues, authorization and SaaS patterns.
APIs, integrations and serverless workloads with careful async design.
Tenancy, permissions, plans, billing and the realities of running SaaS in production.
Workload-first cloud architecture: serverless, storage, data, security and cost.
Build reliable web platforms, SaaS products, dashboards, APIs, and internal tools with modern full-stack technologies.
Learn moreDesign and build SaaS products from MVP to production, including architecture, user management, subscriptions, integrations and cloud infrastructure.
Learn moreGet practical technical guidance for architecture, code quality, performance, accessibility, cloud, and product planning.
Learn moreWhether it's an architecture decision, a delivery problem or something specific to API Development, I can help you find a practical path forward.
What happens next
Share your goal
A few lines about the product, problem, or audit need.
Get a quick read
A practical reply on the best technical path forward.
Move forward
A clear scope, estimate, or concrete next step.