Rivet.Attributes 0.28.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Rivet.Attributes --version 0.28.0
                    
NuGet\Install-Package Rivet.Attributes -Version 0.28.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Rivet.Attributes" Version="0.28.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Rivet.Attributes" Version="0.28.0" />
                    
Directory.Packages.props
<PackageReference Include="Rivet.Attributes" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Rivet.Attributes --version 0.28.0
                    
#r "nuget: Rivet.Attributes, 0.28.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Rivet.Attributes@0.28.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Rivet.Attributes&version=0.28.0
                    
Install as a Cake Addin
#tool nuget:?package=Rivet.Attributes&version=0.28.0
                    
Install as a Cake Tool

<p align="center"> <img src="logo.png" alt="Rivet" width="200" /> <h1 align="center">Rivet</h1> <p align="center"> <a href="https://www.nuget.org/packages/Rivet.Attributes"><img src="https://img.shields.io/nuget/v/Rivet.Attributes?label=Rivet.Attributes" alt="NuGet" /></a> <a href="https://www.nuget.org/packages/dotnet-rivet"><img src="https://img.shields.io/nuget/v/dotnet-rivet?label=dotnet-rivet" alt="NuGet" /></a> <a href="https://github.com/maxanstey-meridian/rivet/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="License" /></a> </p> </p>

End-to-end type safety between .NET and TypeScript. No drift, no schema files, no codegen config.

If .NET can express a type that can be serialised on the wire, Rivet can make it TypeScript on the other side. It maps exactly what can survive a JSON boundary — no more, no less.

oRPC gives you this when your server is TypeScript. Rivet gives you the same DX when your server is .NET.

New here? Follow the Tutorial: Zero to Typed Clientdotnet new webapi to a fully typed TS client in under 5 minutes.

Install

dotnet add package Rivet.Attributes --version "*"
dotnet tool install --global dotnet-rivet

Mark your C# types → get TypeScript types

[RivetType]
public enum Priority { Low, Medium, High, Critical }

[RivetType]
public sealed record Email(string Value); // single-property → branded

[RivetType]
public sealed record TaskItem(Guid Id, string Title, Priority Priority, Email Author);

[RivetType]
public sealed record ErrorDto(string Code, string Message);
// Generated
export type Priority = "Low" | "Medium" | "High" | "Critical";
export type Email = string & { readonly __brand: "Email" };
export type TaskItem = { id: string; title: string; priority: Priority; author: Email };
export type ErrorDto = { code: string; message: string };

Mark your controllers → get a typed client

[RivetClient]
[Route("api/tasks")]
public sealed class TasksController : ControllerBase
{
    [HttpGet("{id:guid}")]
    [ProducesResponseType(typeof(ErrorDto), 404)]
    public async Task<ActionResult<TaskDetailDto>> Get(Guid id, CancellationToken ct) { ... }
}
// Generated — discriminated union, narrowable by status
export type GetResult =
  | { status: 200; data: TaskDetailDto; response: Response }
  | { status: 404; data: ErrorDto; response: Response }
  | { status: Exclude<number, 200 | 404>; data: unknown; response: Response };

const task = await tasks.get(id);                        // → TaskDetailDto (throws on error)
const result = await tasks.get(id, { unwrap: false });   // → GetResult (no throw)

Or define contracts → get compile-time enforcement

// Define the API surface — pure Rivet, no ASP.NET dependency
[RivetContract]
public static class MembersContract
{
    public static readonly RouteDefinition<List<MemberDto>> List =
        Define.Get<List<MemberDto>>("/api/members")
            .Summary("List all team members");
}

// Implement it — compiler enforces the return type matches the contract
[HttpGet]
public async Task<IActionResult> List(CancellationToken ct)
    => (await MembersContract.List.Invoke(async () =>
    {
        return await db.Members.ToListAsync(ct); // must return List<MemberDto>
    })).ToActionResult();

// Works with minimal APIs too — .Route avoids duplicating the route string
app.MapGet(MembersContract.List.Route, async (AppDb db, CancellationToken ct) =>
    (await MembersContract.List.Invoke(async () =>
    {
        return await db.Members.ToListAsync(ct);
    })).ToResult());  // you write ToResult() once, same pattern as ToActionResult()

Add --compile → runtime validation with Zod

dotnet rivet --project Api.csproj --output ./generated --compile

Rivet emits Zod 4 validators backed by fromJSONSchema() — a schemas.ts with standalone JSON Schema definitions and a validators.ts that wraps them:

// schemas.ts — standalone JSON Schema, usable with any validator
import type { core } from "zod";
type JSONSchema = core.JSONSchema.JSONSchema;

const $defs: Record<string, JSONSchema> = { /* all type definitions */ };
export const TaskItemSchema: JSONSchema = { "$ref": "#/$defs/TaskItem", "$defs": $defs };

// validators.ts — cached Zod schemas from the JSON Schema definitions
import { fromJSONSchema, z } from "zod";
import { TaskItemSchema } from "./schemas.js";

const _assertTaskItem = fromJSONSchema(TaskItemSchema);
export const assertTaskItem = (data: unknown): TaskItem => _assertTaskItem.parse(data) as TaskItem;

Every API response is validated at the network boundary — not just primitives, but full object shapes, nested types, and unions. If the server sends unexpected data, you get a clear error immediately — not a silent undefined three components later. Requires zod in your consumer project.

You can also emit just the schemas without validation wiring:

dotnet rivet --project Api.csproj --output ./generated --jsonschema

This writes schemas.ts only — use it with fromJSONSchema(), ajv, or any JSON Schema consumer.

Import OpenAPI → get C# contracts

Another team owns the API? Import their OpenAPI spec, get typed C# contracts, feed them back into the pipeline. The compiler tells you what broke when the upstream spec changes.

{
  "components": {
    "schemas": {
      "TaskDto": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "title": { "type": "string" },
          "priority": { "$ref": "#/components/schemas/Priority" }
        },
        "required": ["id", "title", "priority"]
      },
      "Priority": {
        "type": "string",
        "enum": ["low", "medium", "high", "critical"]
      }
    }
  },
  "paths": {
    "/api/tasks": {
      "get": {
        "tags": ["Tasks"],
        "summary": "List all tasks",
        "responses": {
          "200": {
            "content": { "application/json": { "schema": {
              "type": "array", "items": { "$ref": "#/components/schemas/TaskDto" }
            } } }
          }
        }
      }
    }
  }
}
dotnet rivet --from-openapi spec.json --namespace MyApp.Contracts --output ./src/
// Generated — sealed records, enums, typed contract with builder chain
public enum Priority { Low, Medium, High, Critical }

public sealed record TaskDto(Guid Id, string Title, Priority Priority);

[RivetContract]
public static class TasksContract
{
    public static readonly RouteDefinition<List<TaskDto>> List =
        Define.Get<List<TaskDto>>("/api/tasks")
            .Summary("List all tasks");
}

The importer handles JSON, form-encoded, binary (application/octet-streamIFormFile), and text (text/*string) content types. Endpoints with unsupported or schema-less content types are still generated but annotated with a // [rivet:unsupported ...] comment — see the OpenAPI Import guide for details.

Check contract coverage

dotnet rivet --project Api.csproj --check
warning: [MissingImplementation] MembersContract.Remove: expected DELETE /api/members/{id}, got (none)
warning: [RouteMismatch] MembersContract.UpdateRole: expected /api/members/{id}/role, got /api/members/{id}/update-role
Coverage: 2/4 endpoints covered, 1 mismatch(es), 1 missing.

Verifies that every contract endpoint has a matching handler implementation, with correct HTTP method and route. Useful in CI to catch missing or mismatched handlers.

List your routes

dotnet rivet --project Api.csproj --routes
  Method  Route                      Handler
  ──────  ─────────────────────────  ───────
  GET     /api/members               members.list
  POST    /api/members               members.invite
  DELETE  /api/members/{id}          members.remove
  PUT     /api/members/{id}/role     members.updateRole
4 route(s).

Documentation

Guides, reference, and architecture at maxanstey-meridian.github.io/rivet.

License

MIT

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.34.0 0 4/21/2026
0.33.2 86 4/16/2026
0.33.1 83 4/16/2026
0.33.0 86 4/15/2026
0.32.4 87 4/14/2026
0.32.3 89 4/8/2026
0.32.2 88 4/7/2026
0.32.1 81 4/7/2026
0.32.0 77 4/7/2026
0.31.0 85 4/6/2026
0.30.0 91 3/30/2026
0.29.0 89 3/30/2026
0.28.0 87 3/29/2026
0.27.0 90 3/24/2026
0.26.0 87 3/23/2026
0.25.0 85 3/23/2026
0.24.0 88 3/21/2026
0.22.2 86 3/20/2026
0.22.1 84 3/20/2026
0.22.0 85 3/20/2026
Loading failed