OrionEnvelope
One HTTP contract for the whole API. Every success is a typed { data, meta }, every failure is an RFC 9457 problem+json, and pagination metadata rides in the same envelope — projected mechanically from OrionResult's Result<T>, not hand-serialized per endpoint.
An API with 80 endpoints written by six people has 80 slightly different response shapes: some return the bare object, some { data }, some { result, success, error }; errors are a plain string here, a { message } there, a stack trace in prod somewhere else. The cost is real — every client writes bespoke unwrapping, error handling can't be centralized, and no OpenAPI consumer can generate a clean typed client. ASP.NET Core ships ProblemDetails, but nothing enforces it on every error path and nothing standardizes the success envelope or where pagination lives. OrionEnvelope makes the envelope a projection, not a chore: an endpoint that returns a Result is correctly shaped with zero envelope code.
Features
Envelope<T>—{ "data": ..., "meta": ... }, one success shape for the whole API.- RFC 9457
ProblemDetails— one failure shape, with the family extensionstraceIdand per-fielderrors. Result<T>projection —result.ToEnvelope()on success,result.ToProblemDetails()on failure, with eachOrionResultErrorKindmapped to its HTTP status (validation→400, not-found→404, conflict→409, rate-limited→429, …).- Pagination in the same envelope —
meta.pagecarriesnextCursor/hasMore, so paging is part of the one contract. - Source-gen JSON, AOT-clean — the wire types serialize through a
System.Text.Jsonsource-gen context with no reflection fallback (verified by a NativeAOT smoke test). Multi-targetsnet8.0,net9.0,net10.0.
Install
dotnet add package OrionEnvelope
Usage
Return Result<T> from your services and project it at the boundary:
using Moongazing.OrionEnvelope;
using Moongazing.OrionResult;
Result<OrderDto> result = await orders.GetAsync(id, ct);
if (result.IsSuccess)
{
Envelope<OrderDto> ok = result.ToEnvelope(new Meta { TraceId = traceId });
// 200 → { "data": { ... }, "meta": { "traceId": "..." } }
}
else
{
ProblemDetails problem = result.ToProblemDetails(traceId);
// e.g. 404 application/problem+json → { "type":"about:blank","title":"Not Found","status":404,"code":"...","traceId":"..." }
}
Fold pagination into the same envelope:
var meta = new Meta { TraceId = traceId, Page = new PageMeta(nextCursor, hasMore) };
var envelope = Envelope.Ok(items, meta);
// { "data": [ ... ], "meta": { "traceId": "...", "page": { "nextCursor": "eyJ...", "hasMore": true } } }
The EnvelopeResultFilter / minimal-API filter that applies this automatically (no per-endpoint code), the exception→problem+json mapper, and the OpenAPI schema contribution arrive in later waves; today you call the projection at the boundary.
AOT-clean serialization
Envelope<T> is generic, so its payload type is application-specific: add your payload types to a source-gen context and serialize through the typed JsonTypeInfo.
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
[JsonSerializable(typeof(Envelope<OrderDto>))]
[JsonSerializable(typeof(ProblemDetails))]
public partial class ApiJson : JsonSerializerContext;
// AOT-safe: use the typed JsonTypeInfo overloads, not the JsonSerializerOptions ones.
var json = JsonSerializer.Serialize(envelope, ApiJson.Default.EnvelopeOrderDto);
The library ships OrionEnvelopeJsonContext covering the non-generic types (Meta, PageMeta, ProblemDetails, ProblemFieldError).
Roadmap
This is the Wave 1 foundation (v0.1): the Envelope<T> / Meta / ProblemDetails types, the Result<T> → envelope / problem projection, and source-gen AOT-clean serialization. Later waves add the RFC 9457 exception mapper + a stable error-type-URI taxonomy with production-safe (no-leak) details (W2), the minimal-API + MVC filters that wrap automatically, the OpenAPI schema, and automatic OrionPage Page<T> → meta.page folding (W3, GA), then content negotiation and versioned envelopes (W4). See CHANGELOG.md.
OrionEnvelope defines shapes; it delegates serialization to System.Text.Json. It is not a validation library (validation errors arrive as Result.Error from OrionGuard), not HATEOAS, and HTTP/JSON only.
Versioning
Follows Semantic Versioning. Multi-targets net8.0, net9.0, and net10.0. Binds to OrionResult 0.9.x.
Documentation
- CHANGELOG.md — release notes.
Contributing
Contributions are welcome. See CONTRIBUTING.md and the CODE_OF_CONDUCT.md.
More from the Orion family
Focused .NET libraries built to one quality bar. Each is usable on its own; several share the small Orion.Abstractions contracts spine, but there is no deep dependency web — pick only what you need:
- OrionResult — Result/Option types and a shared error vocabulary (projected by this package)
- OrionPage — keyset/cursor pagination (folds into
meta.page) - Orion.Abstractions — the shared contracts spine: telemetry, options, result, clock
- OrionClock — a
TimeProvider-based clock with TTL / deadline vocabulary - OrionResilience — retry, backoff, and timeout on OrionClock
- OrionRate — rate limiting on OrionClock
- OrionGuard — validation, guard clauses, DDD primitives, domain events
- OrionAudit — automatic EF Core change-audit trail
- OrionBeacon — leader election with fencing tokens
- OrionGrant — permission / authorization checks
- OrionInbox — transactional inbox for exactly-once effects
- OrionKey — source-generated strongly-typed IDs
- OrionLedger — API-key issuance, verification, and rotation
- OrionLens — ambient correlation-context propagation (source of
meta.traceId) - OrionLock — distributed locks with fencing tokens
- OrionOnce — idempotency keys for exactly-once request handling
- OrionPatch — transactional outbox for EF Core
- OrionRelay — outbound webhook delivery (HMAC, retries, backoff)
- OrionSaga — sagas / process managers for long-running workflows
- OrionShade — sensitive-data redaction for logs and telemetry
- OrionStream — server-sent events / streaming hub
- OrionVault — field-level encryption for EF Core
See it all working together in OrionShowcase, a production-shaped banking sample.
License
MIT.
Packages
| Package | Version | Downloads |
|---|---|---|
| OrionEnvelope | 0.1.0 | 113 |