4.4Building a REST API & JWT authentication
The essentials in 30 seconds
- REST = resources + methods:
GET /api/produits(list, 200),POST(create, 201),PUT(update),DELETE(delete) — JSON everywhere. - JWT = signed self-contained token: issued at login, sent back in
Authorization: Bearer …, verified without a session — suited to a separate front end and mobile. - Riad's reflex: JWT secret in an environment variable (
.env, never hardcoded), HTTPS mandatory, validation + clean JSON errors.
Running case — Riad opens his catalogue as an API
Riad's shop wants a mobile app in Constantine: his catalogue must come out as JSON, not HTML. Riad designs his endpoints (GET to list/read, POST to create with 201, PUT, DELETE), returns JSON with the right codes, then secures it: at login he issues a signed JWT whose secret lives in .env, and each mobile request sends it back in the Authorization header. When the token is missing or forged, the API answers 401 without exposing the data. Fourth milestone of the thread: the same back end serves the site and the mobile app.
Continues the 4.1-4.3 case — Riad decouples his back end from its clients.
1. The REST API
An API exposes DATA (JSON) to other programs: separate JavaScript front end (React, Vue), mobile application, other services. The REST style: resources through clear URLs, manipulated by HTTP methods per their semantics — GET /api/produits (list), GET /api/produits/{id} (read), POST /api/produits (create, 201), PUT/PATCH /api/produits/{id} (update), DELETE /api/produits/{id} (delete) — JSON responses with the right codes (200, 201, 400, 401, 404). In PHP you can tinker by hand (router, json_encode, headers, codes) but in real projects you use the frameworks' API tooling (API routing, serialization, validation).
2. Token authentication (JWT)
HTTP is stateless and the API serves varied clients (no guaranteed browser cookies): we authenticate with tokens. The JWT (JSON Web Token): after a successful login, the server issues a cryptographically SIGNED token (which proves identity without being forgeable); the client stores it and sends it back with each request (Authorization: Bearer <token>); the server VERIFIES the signature — without storing a session, hence "self-contained". Ideal for a separate front end, mobile, multi-server setups. Frameworks offer robust API auth systems (do not hand-roll your crypto in production).
Non-negotiable API security: secure short-lived tokens, mandatory HTTPS (tokens travel over the wire), input validation, authorization (permissions, not just identity), consistent JSON errors (codes + messages, no leaks), rate limiting. A well-designed API is predictable, professional and safe — the deliverable of decoupled back-end + front/mobile architectures.
The universal 200 (scene not to reproduce)
Takeaway: 201 when created, 404 when missing, 400 when invalid — JSON says what, the code says how.
🔎 Concrete example — 🔎 Concrete example — Riad's catalogue endpoints
The table Riad pins above his screen:
GET /api/produits → list (200)
GET /api/produits/{id} → read (200, 404 if missing)
POST /api/produits → create (201, 400 if invalid)
PUT /api/produits/{id} → update (200, 404 if missing)
DELETE /api/produits/{id} → delete (204)
Auth → Authorization: Bearer (401 if missing/invalid)
Reading: five lines, five method + URL + code pairs.
Lesson: the action comes from the method, the proof of identity from the token.
Section vocabulary
- REST API
- Interface exposing data (JSON) to other programs (front end, mobile, services); resources via URLs, semantic HTTP methods, status codes.
Simple example: Riad's JSON catalogue serves the site and the mobile application.
What it is not: a server-rendered HTML page only.
Reflex: resource + method + expected code for each endpoint.
- Resources & methods (API)
- GET to list/read, POST to create (201), PUT/PATCH to update, DELETE to delete; JSON with the right codes (200, 201, 400, 401, 404…).
Simple example: Riad creates via POST → 201, reads a missing item → 404.
What it is not: everything via POST with 200 even on error.
Reflex: never 200 for an error, never a verb in the URL.
- Token authentication
- For APIs (stateless, varied clients): after login, a token sent back on each request (Authorization) and verified by the server.
Simple example: Riad's mobile app sends its token back on each catalogue call.
What it is not: a cookie session assumed available everywhere.
Reflex: each sensitive route requires and verifies the token.
- JWT (JSON Web Token)
- Cryptographically SIGNED, self-contained token (no server storage), sent back on each request and verified. Suited to a separate front end and mobile.
Simple example: Riad issues a JWT at login and then verifies its signature.
What it is not: a predictable identifier passed in the query string.
Reflex: verified signature, short lifetime, nothing sensitive inside.
- API security
- Secure tokens, mandatory HTTPS, input validation, authorization (permissions), clean JSON errors, rate limiting.
Simple example: Riad combines HTTPS + validation + clean 401/403 + rate limiting.
What it is not: an API with no HTTPS that accepts everything and answers 200.
Reflex: the token travels: it must travel encrypted and expire fast.
In practice — In practice — Expose data via a REST API
- Design a resource's endpoints per REST: GET to list/read, POST to create (201), PUT to update, DELETE to delete — JSON responses.
- Return JSON with the right codes (json_encode + headers by hand, or a framework's API tooling).
- Secure with JWT: signed token issued at login (secret in .env), verified on each request (Authorization).
- Add HTTPS, validation, authorization, consistent JSON errors and rate limiting.
Ready-to-say script — present your API to the mobile client (30 s)
"This is Riad: our API exposes the catalogue as JSON — GET to read, POST with 201 to create, PUT and DELETE, with the right codes. Login issues a signed JWT whose secret lives in .env, each request sends it in Authorization, and everything travels over HTTPS."
Say it in a demo; show the endpoint table and one call with a token.
Copy-paste template — PHP API + JWT via the environment (tested code)
'Configuration serveur manquante']); exit; }
header('Content-Type: application/json');
$methode = $_SERVER['REQUEST_METHOD'];
$chemin = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
// Login: verifies (hashing in production), issues a short-lived JWT
if ($methode === 'POST' && $chemin === '/api/connexion') {
$corps = json_decode(file_get_contents('php://input'), true) ?? [];
if (empty($corps['email']) || empty($corps['motDePasse'])) {
http_response_code(400); echo json_encode(['erreur' => 'Email et mot de passe requis']); exit;
}
$jeton = JWT::encode(['sub' => $corps['email'], 'iat' => time(), 'exp' => time() + 3600], $secret, 'HS256');
echo json_encode(['jeton' => $jeton]); exit;
}
// Protected route: verifies the JWT (401 if missing/invalid)
if ($methode === 'GET' && $chemin === '/api/produits') {
$entete = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
if (!str_starts_with($entete, 'Bearer ')) { http_response_code(401); echo json_encode(['erreur' => 'Jeton manquant']); exit; }
try {
JWT::decode(substr($entete, 7), new Key($secret, 'HS256'));
} catch (Exception $e) { http_response_code(401); echo json_encode(['erreur' => 'Jeton invalide']); exit; }
echo json_encode([['nom' => 'Couscoussier', 'prix' => 2500]]); exit;
}
http_response_code(404); echo json_encode(['erreur' => 'Introuvable']);
Local prototype
Hand-made JSON endpoints (router + json_encode): REST conventions from the start, they cost nothing.
Real application
Like Riad: framework API tooling, JWT via .env, HTTPS, validation and rate limiting.
Multi-client API
One back end for web + mobile: version (/api/v1/) to evolve without breaking.
Sensitive data
Short token lifetime, revocation planned, nothing sensitive in the payload — and an audit before production.
.env outside Git), never hardcoded in code; mandatory HTTPS (tokens otherwise travel in clear text); short lifetime and systematic validation. Examples with fictitious data only — no real secrets. Teaching content, not a security audit.✓ Best practices
- Design as resources + methods + right codes.
- Authenticate via JWT verified on each request.
- Enforce HTTPS, validation and clean JSON errors.
✗ Common pitfalls
- Returning everything as 200 even on error.
- Exposing an API without HTTPS.
- Hand-rolling your JWT crypto in production.
Key takeaways
- REST API = JSON DATA for front/mobile/services: RESOURCES via URLs, semantic METHODS, correct CODES.
- By hand (router, json_encode) or via FRAMEWORK (API routing, serialization, validation).
- JWT: signed SELF-CONTAINED token (Authorization: Bearer), verified without a session — suited to varied clients.
- SECURITY: secret in .env, HTTPS, validation, authorization, clean errors, rate limiting.
48h challenge — apply it tomorrow
Expose 1 resource as JSON (5 endpoints, right codes) + JWT login (secret in .env) and verify the 401 without a token.
- I can name the 5 endpoints and their codes.
- I can explain JWT vs sessions.
- I have 1 protected route tested with and without a token.
Frequently asked questions
Why build an API rather than a site that generates HTML?
When the consumer is not a browser alone: separate front-end application (React/Vue/Angular, interactive SPA), mobile application (which only consumes JSON), several clients (web + mobile + partners on ONE reused back end), service/microservice architectures. Otherwise, the classic site (server-side HTML) stays simpler. Riad picks the API as soon as a mobile app or a decoupled front end enters the picture.
What is a JWT — and why tokens rather than sessions for APIs?
Sessions: server-side state + browser cookies — good for a classic site, but tied to the browser and complicated at multi-server scale. JWT: signed token issued at login, stored client-side, sent back in Authorization and verified by signature — no server state, suited to varied clients (mobile, services) and to scaling. Warnings: protected key (never hardcoded), short lifetime/refresh, nothing sensitive in the payload, HTTPS.
Why tokens (JWT) rather than sessions for an API?