BelajarKoding Logobelajarkoding

Platform belajar web development Indonesia. Artikel, cheat sheets, roadmap, dan code challenges untuk developer Indonesia.

Navigasi

  • Artikel
  • Cheat Sheets
  • Roadmap
  • Challenges
  • Pricing
  • Search

Produk Lain

  • JagoHermes
  • KelasClaude
  • KilatKoding
  • BelajarVibeCoding
  • JualanKoding

Support

  • Privacy Policy
  • Terms of Service
  • Email

© 2026 BelajarKoding. All rights reserved.

Galih PratamaBagian dari ekosistem Galih Pratama
belajarkoding LogobyGalih Pratama
RoadmapArtikelCheat SheetsChallengesUpgrade
belajarkoding LogobyGalih Pratama
RoadmapArtikelCheat SheetsChallengesUpgrade
belajarkoding LogobyGalih Pratama
RoadmapArtikelCheat SheetsChallengesUpgrade

Daftar Isi

1xx - Informational Response100 Continue101 Switching Protocols102 Processing (WebDAV)103 Early Hints3xx - Redirection300 Multiple Choices301 Moved Permanently302 Found303 See Other304 Not Modified307 Temporary Redirect308 Permanent Redirect4xx - Client Error400 Bad Request401 Unauthorized402 Payment Required403 Forbidden404 Not Found405 Method Not Allowed406 Not Acceptable407 Proxy Authentication Required408 Request Timeout409 Conflict410 Gone411 Length Required412 Precondition Failed413 Payload Too Large414 URI Too Long415 Unsupported Media Type416 Range Not Satisfiable417 Expectation Failed418 I'm a teapot421 Misdirected Request422 Unprocessable Entity (WebDAV)423 Locked (WebDAV)424 Failed Dependency (WebDAV)425 Too Early426 Upgrade Required428 Precondition Required429 Too Many Requests431 Request Header Fields Too Large451 Unavailable For Legal Reasons5xx - Server Error500 Internal Server Error501 Not Implemented502 Bad Gateway503 Service Unavailable504 Gateway Timeout505 HTTP Version Not Supported506 Variant Also Negotiates507 Insufficient Storage (WebDAV)508 Loop Detected (WebDAV)510 Not Extended511 Network Authentication RequiredCheat Sheet by CategorySuccess Responses (2xx)Redirects (3xx)Client Errors (4xx)Server Errors (5xx)Best PracticesChoosing Right Status CodeError Response FormatAuthentication vs AuthorizationCaching HeadersRate LimitingKesalahan UmumWrong: Pakai 200 buat semua responseCorrect: Pakai status code yang sesuaiWrong: 401 vs 403 kebalikCorrect: Bedakan authenticated vs authorizedWrong: Expose error details di productionCorrect: Generic error di productionHTTP Status Code Decision TreeTesting Status CodesUnit testing dengan JestIntegration testingReferences
HTTPAPINetworking

HTTP Status Codes

Referensi lengkap HTTP status codes dari 1xx sampai 5xx yang wajib kamu tau buat web development!

http17 min read3.262 kata
Silakan login atau daftar untuk membaca cheat sheet ini.

#1xx - Informational Response

Status codes yang menunjukkan bahwa request telah diterima dan diproses, tetapi belum selesai.

Respon informasional yang bilang request masih diproses

#100 Continue

Server telah menerima request headers dan client dapat melanjutkan mengirim request body.

http
HTTP/1.1 100 Continue

Server udah terima request headers, client boleh lanjutin kirim body

Kapan dipake:

  • Upload file gede
  • Request dengan body besar
  • Perlu konfirmasi dari server dulu

#101 Switching Protocols

Server setuju untuk beralih protokol, biasanya dari HTTP ke WebSocket atau protokol lain.

http
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade

Server setuju switch protokol (contoh: HTTP ke WebSocket)

#102 Processing (WebDAV)

Server masih memproses request tetapi belum selesai, digunakan untuk mencegah timeout.

http
HTTP/1.1 102 Processing

Server udah terima request, tapi masih proses (gak timeout)

#103 Early Hints

Server memberikan hint kepada client untuk memuat resource sebelum response final dikirim.

http
HTTP/1.1 103 Early Hints
Link: </style.css>; rel=preload; as=style
Link: </script.js>; rel=preload; as=script

Kasih tau client buat preload resource sambil nunggu response final

Use case:

  • Performance optimization
  • Preload critical resources
  • Reduce time to first paint

#2xx - Success

Status codes yang menunjukkan bahwa request berhasil diproses oleh server.

Request berhasil diproses!

#200 OK

Request berhasil dan response body berisi data yang diminta.

http
HTTP/1.1 200 OK
Content-Type: application/json
 
{
 "message": "Success banget!"
}

Request berhasil, ini datanya

Dipake untuk:

  • GET yang sukses
  • POST yang sukses (kalo gak bikin resource baru)
  • PUT/PATCH yang sukses

#201 Created

http
HTTP/1.1 201 Created
Location: /api/users/123
Content-Type: application/json
 
{
 "id": 123,
 "name": "Budi"
}

Resource baru berhasil dibuat

Best practice:

  • Selalu sertakan Location header
  • Return data resource yang baru dibuat
  • Dipake sama POST/PUT

#202 Accepted

http
HTTP/1.1 202 Accepted
Content-Type: application/json
 
{
 "job_id": "abc123",
 "status": "processing"
}

Request diterima tapi belum selesai diproses (async)

Kapan dipake:

  • Background jobs
  • Long-running tasks
  • Async processing

#203 Non-Authoritative Information

http
HTTP/1.1 203 Non-Authoritative Information

Request sukses, tapi data dari proxy/cache (bukan origin server)

#204 No Content

http
HTTP/1.1 204 No Content

Request sukses, tapi gak ada data yang dikembalikan

Use case:

  • DELETE yang sukses
  • PUT/PATCH yang gak perlu return data
  • Endpoint yang cuma trigger action

#205 Reset Content

http
HTTP/1.1 205 Reset Content

Request sukses, client harus reset document view (clear form)

#206 Partial Content

http
HTTP/1.1 206 Partial Content
Content-Range: bytes 0-1023/10240
Content-Length: 1024
 
[partial data...]

Return sebagian data aja (range request)

Dipake untuk:

  • Video/audio streaming
  • Resume download
  • Large file downloads

#207 Multi-Status (WebDAV)

http
HTTP/1.1 207 Multi-Status
Content-Type: application/xml
 
<?xml version="1.0"?>
<multistatus xmlns="DAV:">
 <response>...</response>
</multistatus>

Banyak status buat banyak resource

#208 Already Reported (WebDAV)

http
HTTP/1.1 208 Already Reported

Member udah dilaporin sebelumnya di response yang sama

#226 IM Used

http
HTTP/1.1 226 IM Used
IM: vcdiff

Server udah fulfill request dengan instance manipulation


#3xx - Redirection

Client perlu action lagi buat complete request

#300 Multiple Choices

http
HTTP/1.1 300 Multiple Choices
Content-Type: text/html
 
<ul>
 <li><a href="/doc.html">HTML version</a></li>
 <li><a href="/doc.pdf">PDF version</a></li>
</ul>

Ada banyak pilihan resource

#301 Moved Permanently

http
HTTP/1.1 301 Moved Permanently
Location: https://new-domain.com/page

Resource pindah permanen ke URL baru

Best practice:

  • Update bookmarks & search engines
  • Cache indefinitely
  • Redirect semua future requests

Contoh penggunaan:

javascript
// Express.js
app.get('/old-page', (req, res) => {
 res.redirect(301, '/new-page');
});

#302 Found

http
HTTP/1.1 302 Found
Location: /temporary-page

Resource pindah sementara (temporary redirect)

Kapan dipake:

  • A/B testing
  • Maintenance mode
  • Temporary redirects

#303 See Other

http
HTTP/1.1 303 See Other
Location: /success-page

Redirect ke URL lain pake GET method

Use case:

  • Redirect setelah POST (POST-Redirect-GET pattern)
  • Prevent duplicate submissions
javascript
// After form submission
app.post('/submit', (req, res) => {
 // Process form...
 res.redirect(303, '/success');
});

#304 Not Modified

http
HTTP/1.1 304 Not Modified
ETag: "abc123"
Cache-Control: max-age=3600

Resource gak berubah, pake cache aja

Headers yang diperlukan:

  • If-None-Match (client) + ETag (server)
  • If-Modified-Since (client) + Last-Modified (server)

#307 Temporary Redirect

http
HTTP/1.1 307 Temporary Redirect
Location: /temp-location

Kayak 302, tapi method & body gak berubah

Bedanya sama 302:

  • 302: Boleh ganti method ke GET
  • 307: Method harus tetap sama

#308 Permanent Redirect

http
HTTP/1.1 308 Permanent Redirect
Location: /new-permanent-location

Kayak 301, tapi method & body gak berubah


#4xx - Client Error

Ada yang salah di sisi client nih

#400 Bad Request

http
HTTP/1.1 400 Bad Request
Content-Type: application/json
 
{
 "error": "Invalid JSON syntax"
}

Request gak valid (syntax error, validation error, dll)

Contoh kasus:

  • JSON malformed
  • Missing required fields
  • Invalid parameter types

#401 Unauthorized

http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api"
 
{
 "error": "Token expired"
}

Salah kaprah: Ini bukan "unauthorized", tapi "unauthenticated" Artinya: Kamu belum login atau token invalid

Best practice:

javascript
// JWT authentication
if (!token || !isValidToken(token)) {
 return res.status(401).json({
  error: 'Authentication required'
 });
}

#402 Payment Required

http
HTTP/1.1 402 Payment Required

Reserved buat future use (payment systems)

#403 Forbidden

http
HTTP/1.1 403 Forbidden
 
{
 "error": "You don't have permission to access this resource"
}

Authenticated tapi gak punya akses (gak boleh)

Bedanya sama 401:

  • 401: Belum login
  • 403: Udah login, tapi gak boleh akses

Contoh:

javascript
// Kamu udah login, tapi bukan admin
if (!user.isAdmin) {
 return res.status(403).json({
  error: 'Admin only'
 });
}

#404 Not Found

http
HTTP/1.1 404 Not Found
 
{
 "error": "Resource not found"
}

Resource yang kamu cari gak ada

Custom 404:

javascript
app.use((req, res) => {
 res.status(404).json({
  error: 'Endpoint not found',
  path: req.path
 });
});

#405 Method Not Allowed

http
HTTP/1.1 405 Method Not Allowed
Allow: GET, POST
 
{
 "error": "DELETE method not allowed"
}

Method HTTP gak diizinkan buat endpoint ini

#406 Not Acceptable

http
HTTP/1.1 406 Not Acceptable

Server gak bisa return format yang diminta di Accept header

#407 Proxy Authentication Required

http
HTTP/1.1 407 Proxy Authentication Required
Proxy-Authenticate: Basic realm="proxy"

Client harus auth ke proxy dulu

#408 Request Timeout

http
HTTP/1.1 408 Request Timeout

Client kelamaan gak kirim request

#409 Conflict

http
HTTP/1.1 409 Conflict
 
{
 "error": "Email already exists"
}

Conflict sama state resource saat ini

Contoh penggunaan:

  • Duplicate unique values
  • Version conflicts
  • Concurrent modifications
javascript
// Email udah dipake
if (await User.findOne({ email })) {
 return res.status(409).json({
  error: 'Email already registered'
 });
}

#410 Gone

http
HTTP/1.1 410 Gone
 
{
 "error": "This resource has been permanently deleted"
}

Resource udah gak ada dan gak akan balik lagi (permanen)

Bedanya sama 404:

  • 404: Mungkin ada, mungkin gak
  • 410: Dulu ada, sekarang udah permanently deleted

#411 Length Required

http
HTTP/1.1 411 Length Required

Request butuh Content-Length header

#412 Precondition Failed

http
HTTP/1.1 412 Precondition Failed

Precondition di headers gak terpenuhi

Contoh:

  • If-Match header gak cocok
  • If-Unmodified-Since failed

#413 Payload Too Large

http
HTTP/1.1 413 Payload Too Large
 
{
 "error": "File size exceeds 10MB limit"
}

Request body terlalu gede

Handle di Express:

javascript
app.use(express.json({ limit: '10mb' }));

#414 URI Too Long

http
HTTP/1.1 414 URI Too Long

URL terlalu panjang (biasanya karena query string kegedean)

#415 Unsupported Media Type

http
HTTP/1.1 415 Unsupported Media Type
 
{
 "error": "Only JSON is supported",
 "received": "application/xml"
}

Content-Type yang dikirim gak didukung

#416 Range Not Satisfiable

http
HTTP/1.1 416 Range Not Satisfiable
Content-Range: bytes */10240

Range yang diminta gak valid

#417 Expectation Failed

http
HTTP/1.1 417 Expectation Failed

Server gak bisa fulfill Expect header

#418 I'm a teapot

http
HTTP/1.1 418 I'm a teapot
 
{
 "error": "I'm a teapot, not a coffee maker! ☕"
}

Easter egg! Dari RFC 2324 (April Fools' joke) Teapot gak bisa brew coffee 😄

#421 Misdirected Request

http
HTTP/1.1 421 Misdirected Request

Request dikirim ke server yang gak bisa produce response

#422 Unprocessable Entity (WebDAV)

http
HTTP/1.1 422 Unprocessable Entity
 
{
 "errors": {
  "email": "Invalid email format",
  "age": "Must be at least 18"
 }
}

Request valid tapi semantic error (validation failed)

Bedanya sama 400:

  • 400: Syntax error (JSON malformed, dll)
  • 422: Semantic error (validation error)

Contoh:

javascript
// Validation error
const errors = validateUser(req.body);
if (errors.length > 0) {
 return res.status(422).json({ errors });
}

#423 Locked (WebDAV)

http
HTTP/1.1 423 Locked

Resource terkunci

#424 Failed Dependency (WebDAV)

http
HTTP/1.1 424 Failed Dependency

Request failed karena request sebelumnya gagal

#425 Too Early

http
HTTP/1.1 425 Too Early

Server gak mau proses request yang mungkin di-replay

#426 Upgrade Required

http
HTTP/1.1 426 Upgrade Required
Upgrade: HTTP/2.0

Client harus switch ke protokol lain

#428 Precondition Required

http
HTTP/1.1 428 Precondition Required

Request harus punya precondition headers

#429 Too Many Requests

http
HTTP/1.1 429 Too Many Requests
Retry-After: 3600
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1640000000
 
{
 "error": "Rate limit exceeded. Try again in 1 hour"
}

Rate limiting! Client request terlalu banyak

Headers yang berguna:

  • Retry-After: Kapan boleh coba lagi
  • X-RateLimit-Limit: Limit per window
  • X-RateLimit-Remaining: Sisa quota
  • X-RateLimit-Reset: Timestamp reset

Implementasi:

javascript
// Rate limiting middleware
const rateLimit = require('express-rate-limit');
 
const limiter = rateLimit({
 windowMs: 15 * 60 * 1000, // 15 menit
 max: 100, // max 100 requests
 message: 'Too many requests',
 standardHeaders: true,
 legacyHeaders: false,
});
 
app.use('/api/', limiter);

#431 Request Header Fields Too Large

http
HTTP/1.1 431 Request Header Fields Too Large

Header fields terlalu besar

#451 Unavailable For Legal Reasons

http
HTTP/1.1 451 Unavailable For Legal Reasons
 
{
 "error": "Content blocked due to legal reasons"
}

Resource gak bisa diakses karena alasan hukum (censorship, DMCA, dll)


#5xx - Server Error

Server yang error, bukan salah lo

#500 Internal Server Error

http
HTTP/1.1 500 Internal Server Error
 
{
 "error": "Something went wrong on our end"
}

Generic error - Ada yang error di server

Best practice:

  • Jangan expose stack trace ke client (security risk)
  • Log error details di server
  • Return generic message ke client
javascript
app.use((err, req, res, next) => {
 console.error(err.stack); // Log di server
 res.status(500).json({
  error: 'Internal server error'
  // Jangan: error: err.message (bisa expose sensitive info)
 });
});

#501 Not Implemented

http
HTTP/1.1 501 Not Implemented
 
{
 "error": "This method is not supported yet"
}

Server gak recognize atau gak support method

#502 Bad Gateway

http
HTTP/1.1 502 Bad Gateway

Server (sebagai gateway/proxy) dapat invalid response dari upstream server

Contoh kasus:

  • Nginx gak bisa connect ke backend
  • API gateway timeout
  • Upstream server down

#503 Service Unavailable

http
HTTP/1.1 503 Service Unavailable
Retry-After: 3600
 
{
 "error": "Service temporarily unavailable",
 "retry_after": 3600
}

Server temporarily gak bisa handle request

Kapan dipake:

  • Maintenance mode
  • Server overload
  • Database down
  • Temporary outage

Implementasi maintenance:

javascript
const MAINTENANCE_MODE = process.env.MAINTENANCE === 'true';
 
app.use((req, res, next) => {
 if (MAINTENANCE_MODE) {
  return res.status(503).json({
   error: 'Under maintenance',
   message: 'We will be back soon!'
  });
 }
 next();
});

#504 Gateway Timeout

http
HTTP/1.1 504 Gateway Timeout

Server (sebagai gateway) gak dapat response dari upstream server tepat waktu

Bedanya sama 502:

  • 502: Response invalid
  • 504: Gak ada response (timeout)

#505 HTTP Version Not Supported

http
HTTP/1.1 505 HTTP Version Not Supported

HTTP version yang dipake gak didukung

#506 Variant Also Negotiates

http
HTTP/1.1 506 Variant Also Negotiates

Internal configuration error di content negotiation

#507 Insufficient Storage (WebDAV)

http
HTTP/1.1 507 Insufficient Storage

Server gak bisa store representation yang diperlukan

#508 Loop Detected (WebDAV)

http
HTTP/1.1 508 Loop Detected

Server detect infinite loop saat proses request

#510 Not Extended

http
HTTP/1.1 510 Not Extended

Further extensions required buat fulfill request

#511 Network Authentication Required

http
HTTP/1.1 511 Network Authentication Required
 
{
 "error": "Network authentication required"
}

Client harus auth buat dapat network access

Contoh: Captive portal di WiFi public


#Cheat Sheet by Category

#Success Responses (2xx)

plaintext
200 OK         → Request sukses
201 Created      → Resource dibuat
202 Accepted      → Async processing
204 No Content     → Sukses tapi gak ada data
206 Partial Content  → Partial data (streaming)

#Redirects (3xx)

plaintext
301 Moved Permanently   → Permanent redirect
302 Found         → Temporary redirect (method bisa berubah)
303 See Other      → POST-Redirect-GET
304 Not Modified     → Use cache
307 Temporary Redirect  → Temporary (method tetap)
308 Permanent Redirect  → Permanent (method tetap)

#Client Errors (4xx)

plaintext
400 Bad Request       → Syntax/validation error
401 Unauthorized       → Gak authenticated
403 Forbidden        → Gak authorized
404 Not Found        → Resource gak ada
409 Conflict        → Duplicate/conflict
422 Unprocessable Entity  → Semantic validation error
429 Too Many Requests    → Rate limit exceeded

#Server Errors (5xx)

plaintext
500 Internal Server Error  → Generic server error
502 Bad Gateway      → Invalid upstream response
503 Service Unavailable  → Temporary unavailable
504 Gateway Timeout    → Upstream timeout

#Best Practices

#Choosing Right Status Code

Untuk API endpoints:

javascript
// GET - Retrieve data
app.get('/users/:id', async (req, res) => {
 const user = await User.findById(req.params.id);
 if (!user) return res.status(404).json({ error: 'User not found' });
 res.status(200).json(user);
});
 
// POST - Create resource
app.post('/users', async (req, res) => {
 const user = await User.create(req.body);
 res.status(201)
   .location(`/users/${user.id}`)
   .json(user);
});
 
// PUT - Update (full replacement)
app.put('/users/:id', async (req, res) => {
 const user = await User.findByIdAndUpdate(req.params.id, req.body);
 if (!user) return res.status(404).json({ error: 'User not found' });
 res.status(200).json(user);
});
 
// PATCH - Partial update
app.patch('/users/:id', async (req, res) => {
 const user = await User.findByIdAndUpdate(
  req.params.id,
  { $set: req.body },
  { new: true }
 );
 if (!user) return res.status(404).json({ error: 'User not found' });
 res.status(200).json(user);
});
 
// DELETE - Delete resource
app.delete('/users/:id', async (req, res) => {
 const user = await User.findByIdAndDelete(req.params.id);
 if (!user) return res.status(404).json({ error: 'User not found' });
 res.status(204).send();
});

#Error Response Format

Consistent error structure:

javascript
// Error response standard
{
 "error": {
  "code": "VALIDATION_ERROR",
  "message": "Validation failed",
  "details": [
   {
    "field": "email",
    "message": "Invalid email format"
   }
  ]
 }
}
 
// Implementation
class APIError extends Error {
 constructor(statusCode, code, message, details = []) {
  super(message);
  this.statusCode = statusCode;
  this.code = code;
  this.details = details;
 }
}
 
// Usage
app.post('/register', (req, res, next) => {
 const errors = validateUser(req.body);
 if (errors.length) {
  return next(new APIError(
   422,
   'VALIDATION_ERROR',
   'Validation failed',
   errors
  ));
 }
 // Process registration...
});
 
// Error handler
app.use((err, req, res, next) => {
 const statusCode = err.statusCode || 500;
 res.status(statusCode).json({
  error: {
   code: err.code || 'INTERNAL_ERROR',
   message: err.message,
   details: err.details || []
  }
 });
});

#Authentication vs Authorization

javascript
// 401 - Unauthenticated (belum login)
if (!req.user) {
 return res.status(401).json({
  error: 'Authentication required',
  message: 'Please login first'
 });
}
 
// 403 - Unauthorized (udah login tapi gak boleh)
if (req.user.role !== 'admin') {
 return res.status(403).json({
  error: 'Forbidden',
  message: 'Admin access required'
 });
}

#Caching Headers

javascript
// 304 Not Modified pattern
app.get('/data', (req, res) => {
 const data = getData();
 const etag = generateETag(data);
 
 // Check if client has latest version
 if (req.headers['if-none-match'] === etag) {
  return res.status(304).end();
 }
 
 res.set('ETag', etag)
   .set('Cache-Control', 'max-age=3600')
   .json(data);
});

#Rate Limiting

javascript
// 429 Too Many Requests
const rateLimit = new Map();
 
function checkRateLimit(userId) {
 const now = Date.now();
 const userLimit = rateLimit.get(userId) || { count: 0, resetTime: now + 60000 };
 
 if (now > userLimit.resetTime) {
  userLimit.count = 0;
  userLimit.resetTime = now + 60000;
 }
 
 userLimit.count++;
 rateLimit.set(userId, userLimit);
 
 return {
  allowed: userLimit.count <= 100,
  remaining: Math.max(0, 100 - userLimit.count),
  resetTime: userLimit.resetTime
 };
}
 
app.use((req, res, next) => {
 const limit = checkRateLimit(req.user?.id || req.ip);
 
 res.set({
  'X-RateLimit-Limit': '100',
  'X-RateLimit-Remaining': limit.remaining.toString(),
  'X-RateLimit-Reset': Math.floor(limit.resetTime / 1000).toString()
 });
 
 if (!limit.allowed) {
  return res.status(429).json({
   error: 'Too many requests',
   retry_after: Math.ceil((limit.resetTime - Date.now()) / 1000)
  });
 }
 
 next();
});

#Kesalahan Umum

#Wrong: Pakai 200 buat semua response

javascript
// JANGAN GINI!
app.post('/users', async (req, res) => {
 try {
  const user = await User.create(req.body);
  res.status(200).json(user); // Harusnya 201
 } catch (err) {
  res.status(200).json({ error: err.message }); // Harusnya 4xx/5xx
 }
});

#Correct: Pakai status code yang sesuai

javascript
app.post('/users', async (req, res) => {
 try {
  const user = await User.create(req.body);
  res.status(201)
    .location(`/users/${user.id}`)
    .json(user);
 } catch (err) {
  if (err.code === 11000) { // Duplicate key
   return res.status(409).json({ error: 'User already exists' });
  }
  res.status(500).json({ error: 'Internal server error' });
 }
});

#Wrong: 401 vs 403 kebalik

javascript
// SALAH!
if (!user.isAdmin) {
 return res.status(401).json({ error: 'Not admin' }); // Harusnya 403
}

#Correct: Bedakan authenticated vs authorized

javascript
// Authentication (401)
if (!req.user) {
 return res.status(401).json({ error: 'Please login' });
}
 
// Authorization (403)
if (!req.user.isAdmin) {
 return res.status(403).json({ error: 'Admin only' });
}

#Wrong: Expose error details di production

javascript
// BAHAYA! Jangan expose stack trace
app.use((err, req, res, next) => {
 res.status(500).json({
  error: err.message,
  stack: err.stack // JANGAN!
 });
});

#Correct: Generic error di production

javascript
app.use((err, req, res, next) => {
 // Log di server
 console.error(err);
 
 // Return generic message
 res.status(500).json({
  error: 'Internal server error'
 });
});

#HTTP Status Code Decision Tree

plaintext
Request diterima
  ↓
Syntax valid?
  ├─ No → 400 Bad Request
  └─ Yes
    ↓
  Authenticated?
    ├─ No → 401 Unauthorized
    └─ Yes
      ↓
    Authorized?
      ├─ No → 403 Forbidden
      └─ Yes
        ↓
      Resource exists?
        ├─ No → 404 Not Found
        └─ Yes
          ↓
        Method allowed?
          ├─ No → 405 Method Not Allowed
          └─ Yes
            ↓
          Validation passed?
            ├─ No → 422 Unprocessable Entity
            └─ Yes
              ↓
            Processing successful?
              ├─ No → 500 Internal Server Error
              └─ Yes
                ↓
              Action?
                ├─ Create → 201 Created
                ├─ Delete → 204 No Content
                ├─ Update → 200 OK
                └─ Read → 200 OK

#Testing Status Codes

#Unit testing dengan Jest

javascript
describe('User API', () => {
 test('POST /users - should return 201 on success', async () => {
  const res = await request(app)
   .post('/users')
   .send({ name: 'Budi', email: 'budi@example.com' });
 
  expect(res.status).toBe(201);
  expect(res.body).toHaveProperty('id');
  expect(res.headers.location).toBeDefined();
 });
 
 test('POST /users - should return 409 on duplicate', async () => {
  const userData = { name: 'Budi', email: 'budi@example.com' };
 
  // Create first user
  await request(app).post('/users').send(userData);
 
  // Try to create duplicate
  const res = await request(app).post('/users').send(userData);
 
  expect(res.status).toBe(409);
  expect(res.body.error).toContain('already exists');
 });
 
 test('GET /users/:id - should return 404 for non-existent user', async () => {
  const res = await request(app).get('/users/999999');
 
  expect(res.status).toBe(404);
  expect(res.body.error).toBeDefined();
 });
 
 test('DELETE /users/:id - should return 204 on success', async () => {
  const user = await User.create({ name: 'Budi' });
  const res = await request(app).delete(`/users/${user.id}`);
 
  expect(res.status).toBe(204);
  expect(res.body).toEqual({});
 });
});

#Integration testing

javascript
describe('Error handling', () => {
 test('should return 401 when not authenticated', async () => {
  const res = await request(app)
   .get('/api/protected')
   .set('Authorization', ''); // No token
 
  expect(res.status).toBe(401);
 });
 
 test('should return 403 when not authorized', async () => {
  const token = generateToken({ role: 'user' });
  const res = await request(app)
   .get('/api/admin-only')
   .set('Authorization', `Bearer ${token}`);
 
  expect(res.status).toBe(403);
 });
 
 test('should return 429 when rate limited', async () => {
  // Make 101 requests
  for (let i = 0; i < 101; i++) {
   await request(app).get('/api/data');
  }
 
  const res = await request(app).get('/api/data');
  expect(res.status).toBe(429);
  expect(res.headers['retry-after']).toBeDefined();
 });
});

#References

  • MDN HTTP Status
  • IANA Status Code Registry
  • RFC 9110 - HTTP Semantics
  • REST API Tutorial

Baca Cheat Sheet Lengkap

Login atau daftar akun gratis untuk membaca cheat sheet ini.

LoginDaftar Gratis
Share: