API Guide APIArchitectureDomains JourneyHTTPEndpointsLaravel Form Requests CodeSecurityQuiz
Developer onboarding · Backend fundamentals

APIs, domains and how a frontend talks to a Laravel backend

A visual, hands-on introduction for new developers. Covers APIs, domains, endpoints, Laravel routing, Form Requests, authentication and debugging. Read top to bottom and use the simulators.

1What is an API?

API = Application Programming Interface. A agreed set of rules that lets one program ask another program for data or for an action, without knowing how the other program works inside.

CLIENT Customer = Frontend (browser/app) wants something INTERFACE Waiter = THE API takes orders, brings food SERVER Kitchen = Backend + Database does the real work request response The customer never enters the kitchen. They only use the menu (the API's documented endpoints).
The menu = documentation
Lists what you can ask for: GET /users, POST /login.
The order = request
What you send: which endpoint, which method, what data.
The dish = response
What comes back: usually JSON data plus a status code.
In this guide "API" means a web API: a backend that exposes URLs over HTTP and returns JSON. Others exist (OS APIs, library APIs) but this is the one you will build and call daily.

2The big picture

Almost every modern app is split in three layers. Each layer has one job.

FRONTEND Runs in the user's browser/phone HTML · CSS · JavaScript React · Vue · Flutter Shows things, collects input BACKEND / API Runs on YOUR server PHP/Laravel · Node · .NET Python · Java Rules, security, logic DATABASE Stores everything permanently MySQL · PostgreSQL MongoDB · SQLite Users, loans, invoices… HTTP SQL JSON The frontend NEVER talks to the database directly. Always through the API.
Why never direct? Database passwords would have to live in the browser, where anyone can read them. The API is the guard: it checks who you are and what you are allowed to do before touching data.

3Domains, URLs & DNS

A domain is the human-friendly name of a server (devlapan.com). Computers find each other by IP address (198.51.100.7). DNS is the phone book that translates one into the other.

Anatomy of a URL (hover the parts)

https://api.devlapan.com:443/v1/users/42?active=true
Protocol https: how to talk. HTTPS = encrypted HTTP. Always use it.
Subdomain api: a sub-section you create. www, erp, api, admin can each point to a different server.
Domain name devlapan: the name you buy and own (registrar: Namecheap, GoDaddy…).
TLD com: top-level domain. Others: .na, .co.za, .org.
Port :443: which door on the server. 443 = HTTPS, 80 = HTTP (hidden when default). Dev servers use 3000, 5173, 8000…
Path /v1/users/42: which resource. This is the endpoint.
Query string ?active=true: optional filters, joined with &.

How DNS resolves a name (press play)

Your browserneeds an IP ResolverISP / 8.8.8.8 Rootwho runs .com? .com TLDwho runs devlapan? Nameserverholds your DNS recordsapi → 198.51.100.7
Press play. Result is cached, so next time it is instant.

DNS records you will touch

RecordMeaningExample
Aname → IPv4 addressapi.devlapan.com → 198.51.100.7
CNAMEname → another name (alias)www → devlapan.com
MXwhere email goesmail.devlapan.com
TXTverification, SPF/DKIM email authv=spf1 …
Change a DNS record and the world sees it after the TTL expires (minutes to 48h). That is why "it works for me but not for the client" happens right after a DNS change.

4The full journey: browser to database and back

What really happens when a user opens app.devlapan.com and clicks Login. Step through it.

BrowserFrontend DNSphone book Web serverserves the app files API serverapi.devlapan.com Databaseusers table

  

5HTTP: the language of the web

Every API call is a request followed by exactly one response. Both have the same shape.

Request (browser → API)

POST /v1/login ← method + endpoint
Host: api.devlapan.com
Content-Type: application/json
Authorization: Bearer eyJhbGci… ← who I am

{ "email": "a@b.com",
  "password": "secret" } ← body

Response (API → browser)

HTTP/1.1 200 OK ← status code
Content-Type: application/json

{ "token": "eyJhbGci…",
  "user": { "id": 42,
           "name": "Mike" } }

Methods = the verb (CRUD)

MethodDoesExampleBody?
GETReadGET /users/42no
POSTCreate / submitPOST /usersyes
PUTReplace / updatePUT /users/42yes
DELETERemoveDELETE /users/42no

Status codes = did it work?

2xx Success
200 OK · 201 Created · 204 No content
4xx Client's fault
400 bad data · 401 not logged in · 403 not allowed · 404 not found · 422 validation · 429 too many
5xx Server's fault
500 crash · 502/503 server down · 504 timeout
Rule of thumb: 4xx means fix your request. 5xx means fix the server (check backend logs).

6Endpoints and routes

An endpoint is one specific address on your API that does one specific job. It is the combination of an HTTP method and a URL path. The same path with a different method is a different endpoint.

MethodGET + Base URLhttps://api.devlapan.com + Path/api/v1/invoices/15 = One endpoint"Get invoice number 15"

One resource, five endpoints (REST)

A resource is a thing your system manages: invoices, loans, customers. REST gives every resource the same set of endpoints, so the team always knows where things are.

MethodEndpointWhat it doesLaravel controller methodSuccess code
GET/api/v1/invoicesList invoices (paginated)index200
POST/api/v1/invoicesCreate an invoicestore201
GET/api/v1/invoices/{id}Read one invoiceshow200
PUT/api/v1/invoices/{id}Update an invoiceupdate200
DELETE/api/v1/invoices/{id}Delete an invoicedestroy204
In Laravel one line creates all five: Route::apiResource('invoices', InvoiceController::class); Run php artisan route:list to see every endpoint your app exposes.
Naming rules
  • Use plural nouns: /invoices, not /getInvoice.
  • The method is the verb. Never put verbs in the path.
  • Lowercase, hyphens for words: /loan-applications.
  • Nest only when it belongs: /customers/7/loans.
  • Version it: /v1/… so you can change things later without breaking old apps.
Two kinds of input in the URL
Path parameter
Identifies one record
/invoices/15
Query parameter
Filters, sorting, paging
/invoices?status=paid&page=2

Data you are creating goes in the JSON body, never in the URL.

Laravel automatically prefixes everything in routes/api.php with /api. Whether your final URL is api.devlapan.com/api/v1/… or api.devlapan.com/v1/… depends on your server setup, so confirm it with route:list.

7Inside Laravel: what happens to a request

A request passes through a fixed pipeline of layers. Each layer has one job and can stop the request early with an error. Click a layer.

Request Middleware Route Form Request Controller Service Model / DB Resource 401 / 429404 / 405403 / 422409 / 422404 / 500 not logged inno such URLbad inputbusiness rulenot found / DB Red exits stop the request early. Your controller and service only run if every earlier layer said yes.

Where things live in a Laravel project

my-api/ ├─ routes/ │ └─ api.php endpoints are declared here ├─ app/ │ ├─ Http/ │ │ ├─ Controllers/ InvoiceController.php │ │ ├─ Requests/ StoreInvoiceRequest.php │ │ ├─ Resources/ InvoiceResource.php │ │ └─ Middleware/ │ ├─ Services/ InvoiceService.php (business logic) │ ├─ Models/ Invoice.php, User.php │ └─ Policies/ InvoicePolicy.php (who may do what) ├─ database/ │ ├─ migrations/ table structure, versioned │ └─ seeders/ ├─ config/ cors.php, sanctum.php, database.php └─ .env secrets: DB password, APP_KEY
Artisan commands you will use daily
php artisan serverun locally on :8000
php artisan route:listlist all endpoints
php artisan make:controller InvoiceController --apinew controller
php artisan make:request StoreInvoiceRequestnew form request
php artisan make:resource InvoiceResourcenew API resource
php artisan make:model Invoice -mmodel + migration
php artisan migrateapply DB changes
php artisan config:clearafter editing .env

8Form Requests: validation done properly

A Form Request is a dedicated class that validates the user's input and checks permission before anything else happens. Laravel runs it automatically before your controller or service is called. If anything is wrong, the request is rejected right there, so bad data can never reach your business logic or your database.

Without a Form Request
Client sends amount = "abc" ↓ Controller runs ↓ Service runs (sends email, writes log…) ↓ Database: Incorrect decimal value 'abc' ↓ 500 Server Error, half-finished work, unhelpful message for the user
Errors happen deep in the system, after side effects have already started.
With a Form Request
Client sends amount = "abc" ↓ Form Request checks the input ↓ 422: "The amount field must be a number." ↓ Controller, service and database never run
Rejected at the door. Nothing is saved, no emails go out, and the user gets a clear message.
1. authorize()
"Is this user allowed to do this?" Returns true or false. False gives 403 Forbidden.
2. rules()
"Is every field present, the right type, the right format and within limits?" Failure gives 422 with an error per field.
3. validated()
Only data that passed the rules is handed on. The controller receives a clean, trusted array.
Fail fast. The earlier a bad request is rejected, the cheaper and safer it is. Checking input is the Form Request's only job. It does not save anything, send anything or apply business rules. That is what the service is for.

Try it: POST /api/v1/invoices with StoreInvoiceRequest

Request body
// rules()
'customer_email' => required|email
'amount'         => required|numeric|min:1
'due_date'       => required|date|after:today
What the client receives
Press "Send request".
The frontend should read errors.<field> from the 422 response and show each message under the matching input. Frontend checks are for convenience. The Form Request is the real protection, because anyone can bypass the browser and call the API directly.

Where does the service fit in?

A service is a plain PHP class (usually in app/Services) that holds your business logic: the real rules and steps of what your system does. The controller handles the web part, the service does the actual work.

Form RequestIs the input valid?Is the user allowed?rejects bad input ControllerReceives the requestCalls the serviceHTTP in, HTTP out ServiceBusiness rulesMulti-step workflowsthe "what we do" ModelReads and writesone DB tabledata access only ResourceShapes the JSONyou send backoutput only Each layer has one job, so each can be changed and tested alone. Example, creating an invoice: the service numbers it, saves it inside a transaction, queues the email.
What belongs in a service
  • Steps that must succeed or fail together (save invoice + generate number + queue email) inside a database transaction.
  • Business rules: "a paid invoice cannot be edited", "loan limit is N$500".
  • Calls to outside systems: payment gateway, SMS, email.
  • Logic needed in more than one place: API, admin page, scheduled job, artisan command.
Why not put it all in the controller?
  • Controllers become hundreds of lines and hard to read.
  • You cannot reuse the logic from a queued job or a cron command.
  • Testing needs a fake HTTP request instead of a simple method call.

For a very simple create-and-save you can skip the service. Add one when logic grows beyond a line or two.

Laravel injects the service for you. Type-hint it in the controller constructor (private InvoiceService $invoices) and Laravel builds it automatically. This is called dependency injection.

Complete example: building the "create invoice" endpoint

The files below work together. First the setup (route, migration, model), then the layers a request passes through: Form Request, Controller, Service, Resource.

Use $request->validated(), never $request->all(). all() includes fields the user invented ("status":"paid", "user_id":1). validated() returns only the fields listed in rules(). Pair it with $fillable on the model.

Key terms

TermMeaning
RouteThe line in routes/api.php that connects a method + URL to controller code.
ControllerReceives the request, calls the model, returns a response. Should stay short.
Form RequestValidates the input and checks permission for one endpoint. Runs before the controller and service and rejects bad requests with 422 or 403.
ServiceA plain PHP class holding business logic and multi-step workflows. Called by controllers, jobs and commands.
Model (Eloquent)PHP class that represents one DB table. Invoice::find(1) instead of raw SQL.
MigrationVersioned script that creates or changes tables, so every developer has the same database.
API ResourceDecides exactly which fields appear in the JSON output and how they are formatted.
MiddlewareA gate on the route: authentication, rate limiting, CORS.
PolicyRules for "may this user view/update/delete this specific record?"
SanctumLaravel's token authentication package. Issues the API token the frontend sends in the Authorization: Bearer header.

9Calling the API from the frontend

How the browser side sends the create-invoice request and handles the three outcomes: success, validation errors (422) and other failures.

Try a real public API (leaves your computer)

Click the button.

10CORS, environments & security

CORS: "blocked by CORS policy"

The browser only lets app.devlapan.com call api.devlapan.com if the API says so with a header. It is the backend that must allow it.

Access-Control-Allow-Origin: https://app.devlapan.com

Different subdomain, port or protocol counts as a different "origin". Postman/curl ignore CORS; only browsers enforce it.

Environments
Localhttp://localhost:8000
Staginghttps://staging-api.devlapan.com
Productionhttps://api.devlapan.com

Keep the base URL in an env var (VITE_API_URL), never hard-coded.

Security checklist

  • Always HTTPS in production.
  • Never put secrets (DB passwords, API keys, private keys) in frontend code. Anyone can read it with View Source.
  • Validate on the backend too. Frontend validation is only for user comfort; attackers skip it.
  • Check permissions on every endpoint, not just "is logged in" but "is allowed to see this record".
  • Hash passwords, rate-limit login, return generic error messages.
  • Never log tokens or passwords.

Debugging toolkit

Browser DevTools → Network
See every request, status, headers, payload and response. First place to look.
Console
JavaScript and CORS errors show in red.
Postman / curl
Test the API alone. If it works there but not in the app, the bug is in the frontend (or CORS).

11Check yourself