Skip to content

feat: structured body-parse error log (#288) and client-IP extraction helper (#289) - #304

Closed
ijeoma270 wants to merge 1 commit into
accesslayerorg:mainfrom
ijeoma270:feat/issues-288-289
Closed

feat: structured body-parse error log (#288) and client-IP extraction helper (#289)#304
ijeoma270 wants to merge 1 commit into
accesslayerorg:mainfrom
ijeoma270:feat/issues-288-289

Conversation

@ijeoma270

Copy link
Copy Markdown
Contributor

Closes #288
Closes #289

What changed

#288 — Structured log for request body parse failure (src/middlewares/body-parse-error.middleware.ts)

Added a dedicated Express error-handler middleware that intercepts body-parsing failures on mutation endpoints and emits a structured error-level log before returning the client response.

What is logged:

What is NOT logged:

  • Raw request body (never read or forwarded)
  • Request headers beyond standard Express fields

Behaviour:

  • Fires only on POST, PUT, PATCH, DELETE (mutation methods)
  • GET, HEAD, and all non-parse errors pass through to next() unchanged
  • Client response is unchanged: 400 for malformed JSON, 413 for oversized payloads
  • Mounted in app.ts immediately after express.json() — before routes and the global error handler

Tests (14): all mutation verbs, GET passthrough, generic error passthrough, entity.too.large handling, assertion that body is absent from log, client response shape


#289 — Client IP extraction helper (src/utils/client-ip.utils.ts)

Added getClientIp(req, trusted?) — a small, reusable helper that reads the real client IP from X-Forwarded-For when the socket comes from a trusted proxy, falling back to the direct socket address otherwise.

Resolution order:

  1. If req.socket.remoteAddress is a trusted proxy (loopback, RFC-1918, IPv6 ULA), read the first (leftmost) IP from X-Forwarded-For
  2. Fall back to the direct socket address

Trust model:

  • Defaults to matching private/loopback CIDR ranges only — untrusted public IPs cannot spoof forwarded headers
  • Accepts an optional trusted predicate override for tests or custom deployments

Integration points:

  • request-logger.middleware.tsclientIp field added to every request log entry
  • body-parse-error.middleware.tsclientIp included in parse-failure error logs

Tests (11): trusted loopback/private ranges, multi-hop X-Forwarded-For, array-valued header, custom predicate, untrusted socket passthrough, missing socket edge cases

How to test

# Run the new test suites
pnpm exec jest --testPathPatterns="client-ip.utils|body-parse-error" --no-coverage

# Verify end-to-end: send malformed JSON to a mutation endpoint
curl -X POST http://localhost:3000/api/v1/creators \
  -H "Content-Type: application/json" \
  -d '{bad json}'
# → server logs should contain type:"body_parse_failure" with path + requestId
# → client receives 400 {"success":false,"message":"Invalid JSON in request body"}

Issue accesslayerorg#288 — src/middlewares/body-parse-error.middleware.ts
- New error-handler middleware that intercepts body-parsing failures
  (SyntaxError with body, entity.too.large) exclusively on mutation
  methods (POST, PUT, PATCH, DELETE)
- Emits logger.error with: type, method, path, requestId, clientIp,
  errorType — never logs the raw body or request headers
- Returns 400 / 413 JSON to the client unchanged; non-parse errors and
  GET/HEAD/OPTIONS pass straight through to next()
- Mounted in app.ts immediately after express.json() so parse errors are
  caught before any route or the global error handler sees them
- 14 tests covering: all mutation verbs, GET passthrough, generic errors,
  entity.too.large, no-body-in-log assertion, client response shape

Issue accesslayerorg#289 — src/utils/client-ip.utils.ts
- getClientIp(req, trusted?) reads the first IP from X-Forwarded-For when
  the socket address belongs to a trusted proxy (loopback, RFC-1918, IPv6
  ULA); falls back to req.socket.remoteAddress otherwise
- Accepts an optional predicate override for testing and custom deployments
- Integrated into request-logger.middleware.ts (clientIp field in every
  request log entry) and body-parse-error.middleware.ts (clientIp in
  parse-failure logs)
- 11 tests: trusted loopback/private ranges, array header, custom predicate,
  untrusted socket passthrough, missing socket edge cases

Closes accesslayerorg#288
Closes accesslayerorg#289
@drips-wave

drips-wave Bot commented May 27, 2026

Copy link
Copy Markdown

@ijeoma270 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@ijeoma270

Copy link
Copy Markdown
Contributor Author

Closing in favour of separate PRs per issue.

@ijeoma270 ijeoma270 closed this May 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add helper for extracting client IP from forwarded headers in request context Add structured log for request body parse failure on mutation endpoints

1 participant