Skip to main content

Introduction

OPRA (Open Platform for Rich APIs) is a TypeScript framework for building type-safe, schema-driven APIs across multiple transports. You define your data types and operations once; OPRA takes care of validation, transformation, error handling, documentation, and protocol-specific encoding for every transport your application uses.


The problem

Modern TypeScript applications typically stitch together several independent libraries to cover what should be a single concern — the API contract:

  • A schema or decorator library to define types
  • A validation library to enforce them at runtime
  • A transformation library to coerce incoming data
  • A documentation generator to expose them to clients
  • Separate adapters and boilerplate for each transport

These libraries do not share a common model. A field added to a DTO must be annotated in each library separately to be validated, transformed, and documented correctly. When they drift, the contract your documentation describes no longer matches what your code enforces.


What OPRA provides

OPRA replaces this fragmented stack with a single, unified contract layer:

One schema, all transports

Define your types and operations once using OPRA decorators. The same schema drives:

  • HTTP — path and query parameter parsing, request body decoding, response encoding
  • Kafka — message payload decoding and dispatch
  • RabbitMQ — AMQP message decoding, content-encoding handling, RPC reply
  • WebSocket (Socket.IO) — event argument decoding, acknowledgement encoding

You add a transport adapter; OPRA handles the rest.

Automatic request validation and transformation

Every incoming request — path parameters, query parameters, headers, and body — is parsed, validated, and type-coerced against the declared schema before your handler is ever called. For heavy payloads such as file uploads and multipart forms, OPRA streams parts sequentially, stores files in temporary storage, and cleans them up automatically after the request completes.

Guaranteed response contract

Every outgoing response is encoded against the declared response type. Response types, HTTP status codes, and error shapes are declared alongside the operation and enforced at runtime — what the documentation describes is what the client receives.

Integrated data layer

For data-driven applications, OPRA manages the full pipeline from endpoint to data service to response. Database adapters for MongoDB, SQL (via SQB), and Elasticsearch plug directly into the request lifecycle. Query parameters like filtering, sorting, and projection are parsed from the request and passed to the data service automatically — no manual wiring between the HTTP layer and the database layer.

Powerful data modeling

OPRA's type system goes far beyond simple DTOs. You build your domain model once using TypeScript classes and decorators, and the runtime enforces it everywhere — across every transport, every endpoint, every direction.

Rich data types. OPRA provides four fundamental type categories, each serving a distinct modeling purpose:

Simple types are the built-in primitives — string, number, integer, boolean, bigint — extended by a rich library of semantic types such as uuid, email, url, date, date-time, date-time-tz, base64, ip, iban, credit-card, mobile-phone, object-id, and more. Each carries its own format rules, coercion logic, and validation semantics out of the box.

Complex types are class-based composite structures defined with @ComplexType() and @ApiField(). Each field declares its type, nullability, required status, default value, and constraints such as minimum, maximum, pattern, and length bounds. Complex types support full inheritance — a subtype extends its parent and inherits all field definitions, which can be overridden or extended.

Enum types are defined with the EnumType() factory. The runtime validates that every incoming value belongs to the declared set and rejects anything outside it, regardless of transport.

Mixin types combine multiple complex types into a single composite structure, similar to TypeScript intersection types. They allow you to assemble reusable field groups and compose them into richer models without inheritance.

Full TypeScript compatibility. OPRA mirrors the TypeScript utility type system with first-class schema equivalents. PartialType makes every field of a type optional. RequiredType enforces all fields as required. OmitType removes a set of fields from an existing type. PickType retains only the specified fields. These are not runtime wrappers — they produce new schema types that participate fully in validation, coercion, and documentation, keeping your API contract consistent with your TypeScript types without duplication.

Inheritance and composition. Complex types compose and extend naturally. A subtype inherits all field definitions from its parent and can add or override fields. Nested types are resolved recursively — you model your domain the way it actually is, not the way a particular serializer prefers it.

Enum types. Enums are first-class citizens with EnumType(). The runtime validates that incoming values are members of the declared set and rejects anything outside it, regardless of transport.

Scope-based validation. Validation rules can be scoped to specific contexts — for example, requiring a field on creation but treating it as optional on patch, or exposing sensitive fields only to authenticated scopes. A single type definition covers all variants; the active scope is resolved per request.

Cross-document references. Large systems spread their types across multiple ApiDocument instances. OPRA resolves named references at runtime, so a shared model library can be maintained independently and referenced by any number of API documents without duplication.

Automatic coercion. Incoming strings are coerced to their declared types — a query parameter declared as integer arrives in your handler as a number, a date-time field arrives as a Date, a boolean string "true" becomes true. No manual parsing, no parseInt, no new Date().


Living API documentation

API documentation is generated directly from the schema. There are no separate Swagger decorators to maintain and no risk of drift between documentation and implementation.


Type Safe Client Applications

For frontend and Node.js client applications, OPRA provides @opra/client — a generated, type-safe HTTP client that speaks the OPRA protocol natively. Every request and response is validated and typed against the server schema. Filtering, sorting, projection, and pagination parameters are expressed as structured TypeScript objects rather than raw query strings, eliminating an entire class of runtime errors that typically only surface in integration tests or production.

Clients can be used against any OPRA HTTP server regardless of the underlying framework. The contract lives in the schema; the client enforces it end-to-end.

Schema and code generation

Every OPRA application exposes its contract as a machine-readable ApiDocument — a structured description of all types, operations, parameters, and response shapes. This schema is the single source of truth for everything the API does.

From this schema, OPRA's code generation tooling produces fully typed client libraries for TypeScript applications. Generated clients expose every operation as a strongly-typed method — parameters, request bodies, and return types are all inferred directly from the schema. There are no hand-written fetch wrappers, no any casts, and no risk of client and server drifting apart. When the server schema changes, regenerating the client surfaces breaking changes at compile time before they reach production.

The same schema that drives code generation also drives the interactive API documentation — no separate annotation layer required.



What's Next

Now that you have a clear picture of what OPRA offers, the best way to experience it is to build something. The Quick Start walks you through setting up your first API end-to-end — schema, controller, transport, and client — in just a few minutes.

Quick Start →