Skip to content

Interfaces, operations, and transport

Typra uses TypeSpec-native interface and op declarations for callable seams. Models remain the data contract; operations describe how host-owned runtime code is called; operation decorators describe runtime-only details that should not become serialized model fields.

import "@typra/emitter";
model RenderRequest {
prompt: string;
}
model RenderResult {
text: string;
}
interface Renderer {
render(request: RenderRequest): RenderResult;
}
TypeSpecTypra mapping
interface RendererA callable contract named Renderer.
render(request: RenderRequest)A generated callable operation with a typed request parameter.
RenderResult return typeA typed success result loaded through generated model helpers.

Generated targets project this shared callable contract into idiomatic protocol, interface, trait, or stub surfaces. Host apps provide runtime behavior behind the generated seam.

interface Renderer {
@runtimeCancellable
render(request: RenderRequest): RenderResult;
@sync
preview(request: RenderRequest): string;
@effect(#{ atomic: true })
commit(result: RenderResult): void;
@optionalOperation
@effect(#{ nonFatal: true })
observe(result: RenderResult): void;
}
DecoratorTypra mapping
@runtimeCancellableAdds a host-owned cancellation parameter, such as AbortSignal, CancellationToken, Go context.Context, or a configured runtime token path.
@syncMarks the operation as synchronously callable where the target has distinct sync/async surfaces.
@effect(#{ atomic: true })Records that the operation is an atomic effect boundary. Typra records metadata; the host app owns transaction policy.
@effect(#{ nonFatal: true })Records that operation failures are non-fatal metadata. The host app owns failure handling policy.
@optionalOperationMarks the operation as optional on generated callable surfaces.

These decorators are operation metadata. They do not alter model load/save shape, do not become transport parameters, and do not make Typra responsible for transactions, retries, token acquisition, credential storage, or observability.

Use @vector when an operation needs executable conformance evidence. Vectors can be authored inline or loaded from a constant vector set.

const RenderVectors = #[
#{
name: "basic-render",
stage: "render",
input: #{ request: #{ prompt: "Hello, Typra" } },
expected: #{ text: "Hello, Typra" }
}
];
interface Renderer {
@vector(RenderVectors)
render(request: RenderRequest): RenderResult;
}

Typra lowers vectors into language-neutral conformance records. Generated tests compare each target’s observable transcript against the vector. Vector input/expected are opaque evidence, compared structurally rather than typed against the operation’s parameters or round-tripped through model load()/save(), so authored inputs may be sparse (omitting required-with-default fields the operation ignores). Model-typed data fidelity is proven separately by @sample data conformance.

Some existing Typra contracts may describe callable seams with model-level decorators. Convert those declarations to TypeSpec-native interfaces and operations before adding new coverage.

Previous decorator formTypeSpec-native form
@@protocol(Renderer); model Renderer {}interface Renderer { ... }
@@method(Renderer, "render", "RenderResult", "...", #{ request: "RenderRequest" })render(request: RenderRequest): RenderResult;
optional: true argument on @@method@optionalOperation on the operation.
sync: true argument on @@method@sync on the operation.
#{ runtimeCancellable: true } options@runtimeCancellable on the operation.
#{ atomic: true } options@effect(#{ atomic: true }) on the operation.
#{ nonFatal: true } options@effect(#{ nonFatal: true }) on the operation.

Before:

model RenderRequest {
prompt: string;
}
model RenderResult {
text: string;
}
@@protocol(Renderer);
@@method(
Renderer,
"render",
"RenderResult",
"Render a prompt.",
#{ request: "RenderRequest" },
false,
false,
#{ runtimeCancellable: true }
);
@@method(
Renderer,
"observe",
"void",
"Observe a rendered result.",
#{ result: "RenderResult" },
true,
false,
#{ nonFatal: true }
);
model Renderer {}

After:

model RenderRequest {
prompt: string;
}
model RenderResult {
text: string;
}
interface Renderer {
@runtimeCancellable
render(request: RenderRequest): RenderResult;
@optionalOperation
@effect(#{ nonFatal: true })
observe(result: RenderResult): void;
}

Conversion rules:

  1. Keep the existing model names and parameter names unless they conflict with TypeSpec operation syntax.
  2. Replace string type names in @@method parameters and returns with real TypeSpec type references.
  3. Move runtime-only booleans and effect options onto operation decorators.
  4. Keep @vector data on the operation that owns the behavior case.
  5. Keep HTTP metadata on TypeSpec HTTP decorators such as @route, @path, @query, @header, @cookie, @body, and @statusCode.

Transport projection consumes official TypeSpec HTTP metadata. Typra does not add a separate route or parameter vocabulary.

import "@typespec/http";
using TypeSpec.Http;
@route("/renders")
interface RenderApi {
@post
create(
@header requestId: string,
@cookie session: string,
@body request: RenderRequest
): {
@statusCode statusCode: 201;
@body result: RenderResult;
} | {
@statusCode statusCode: 400;
@body error: RenderError;
};
}
model RenderError {
message: string;
}
HTTP constructTypra mapping
@route, @get, @post, etc.Producer and consumer route/verb metadata.
@pathURI path binding.
@queryQuery string binding.
@headerHeader binding.
@cookieCookie binding.
@bodyRequest or response body binding.
@statusCodeExact response status selection.

Generated consumers hydrate only the declared success response body for the matching status. Non-matching 2xx responses and non-2xx responses remain on the error seam with the original status and body, so error payloads are not loaded as success models.

export interface Renderer {
render(request: RenderRequest, signal?: AbortSignal): Promise<RenderResult>;
preview(request: RenderRequest): string;
commit(result: RenderResult): Promise<void>;
observe?(result: RenderResult): Promise<void>;
}