Handle errors
Facade operations use Effect’s error channel. Error objects have a stable
consumer-facing code and no internal _tag.
import { createCheckout } from "@byfungsi/sapa";import { Effect } from "effect";
const response = createCheckout(command).pipe( Effect.match({ onSuccess: (checkout) => ({ status: 201, body: checkout, }), onFailure: (error) => { switch (error.code) { case "invalid_input": return { status: 400, body: { code: error.code } }; case "offer_not_found": case "price_not_found": case "subscription_not_found": case "invoice_not_found": return { status: 404, body: { code: error.code } }; case "idempotency_conflict": return { status: 409, body: { code: error.code } }; case "provider_unavailable": case "provider_error": case "outcome_unknown": case "internal_error": return { status: 503, body: { code: error.code } }; default: return { status: 422, body: { code: error.code } }; } }, }),);The fallback groups operation-specific business rejections for brevity. In a
production API, map every code intentionally and add an exhaustive never
check when your response contract needs compile-time drift detection.
Logging
Section titled “Logging”Log the operation name, safe identities, and code. Do not log:
- raw payment notifications or signatures;
- provider credentials or raw provider responses;
- raw license keys;
- signed download URLs;
- unbounded parse issue trees;
- customer payloads by default.
internal_error deliberately hides storage corruption, driver failures, and
other implementation detail. Correlate it with private structured telemetry,
not a more revealing public response.