https://github.com/maxanstey-meridian/rivet
Your C# is the contract. Roslyn-derived OpenAPI 3.1 from ASP.NET endpoints or contract-first definitions — typed TS clients via the OpenAPI ecosystem
https://github.com/maxanstey-meridian/rivet
api-client aspnetcore branded-types code-generation codegen csharp developer-tools dotnet full-stack roslyn runtime-validation sealed-records type-safety typesafe typescript zod
Last synced: 16 days ago
JSON representation
Your C# is the contract. Roslyn-derived OpenAPI 3.1 from ASP.NET endpoints or contract-first definitions — typed TS clients via the OpenAPI ecosystem
- Host: GitHub
- URL: https://github.com/maxanstey-meridian/rivet
- Owner: maxanstey-meridian
- Created: 2026-03-18T12:28:37.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-06-12T11:55:12.000Z (17 days ago)
- Last Synced: 2026-06-12T12:07:00.857Z (17 days ago)
- Topics: api-client, aspnetcore, branded-types, code-generation, codegen, csharp, developer-tools, dotnet, full-stack, roslyn, runtime-validation, sealed-records, type-safety, typesafe, typescript, zod
- Language: C#
- Homepage: https://maxanstey-meridian.github.io/rivet/guides/tutorial
- Size: 7.31 MB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
Awesome Lists containing this project
README
Rivet
**Your C# is the contract.** Rivet reads your compiled C# with Roslyn and emits an
OpenAPI 3.1 spec — no runtime reflection, no attributes-on-everything, no drift
between the code and the spec. The OpenAPI ecosystem does the rest: TypeScript
types, a typed fetch client, Zod schemas, rendered docs.
[oRPC](https://orpc.unnoq.com) gives you this when your server is TypeScript.
Rivet gives you the same DX when your server is .NET.
```bash
dotnet add package Rivet.Attributes
dotnet tool install --global dotnet-rivet
```
## Two ways in
### Already have an ASP.NET API? Annotate it.
Mark the endpoints you want surfaced — the operation is derived from the real
transport shape (routes, params, bodies, response types):
```csharp
[ApiController]
[Route("api/tasks")]
public sealed class TasksController : ControllerBase
{
[RivetEndpoint]
[HttpGet("{id:guid}")]
[ProducesResponseType(typeof(TaskDetailDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(NotFoundDto), StatusCodes.Status404NotFound)]
public async Task Get(Guid id, CancellationToken ct) { ... }
}
```
That becomes `GET /api/tasks/{id}` with a typed `200` and `404` — route
constraints normalised, params classified, multipart and form bodies handled.
### Starting fresh? Write the contract first.
A contract is plain C#: routes, inputs, outputs, and error responses in one
place, as data:
```csharp
[RivetContract]
public static class MembersContract
{
public static readonly RouteDefinition> List =
Define.Get>("/api/members");
public static readonly RouteDefinition Invite =
Define.Post("/api/members")
.Status(201)
.Returns(422, "Validation failed")
.Secure("admin");
}
```
Handlers execute the contract, so the compiler enforces that your implementation
matches the declaration — input type, output type, and (at runtime, on the
typed-results path) status codes:
```csharp
[HttpPost]
public async Task Invite([FromBody] InviteMemberRequest request, CancellationToken ct)
=> (await MembersContract.Invite.Invoke(request, async req =>
{
// req is InviteMemberRequest; must return InviteMemberResponse — compiler-enforced
return new InviteMemberResponse(Guid.NewGuid());
})).ToActionResult();
```
Either way — annotated endpoints, contracts, or a mix — the spec comes out the same.
## Generate
```bash
dotnet rivet --project path/to/Api.csproj --output ./generated
```
Writes `./generated/openapi.json`, derived from the compiled C# via the Roslyn
semantic model. Value-object brands, generics, nullability, validation
attributes, polymorphic hierarchies (`oneOf` + discriminator), dictionary key
types, headers, descriptions, and examples all flow into the spec.
## Consume
The spec plugs straight into the OpenAPI TypeScript ecosystem:
```bash
npx openapi-typescript ./generated/openapi.json -o ./src/api/schema.d.ts
npm install openapi-fetch
```
```ts
import createClient from "openapi-fetch";
import type { paths } from "./api/schema";
const api = createClient({ baseUrl: "https://api.example.com" });
// Path, params, body, and per-status responses all inferred.
const { data, error } = await api.GET("/api/tasks/{id}", {
params: { path: { id: taskId } },
});
if (error) {
// narrowed to NotFoundDto for the declared 404
console.error(error.message);
}
```
Docs via any OpenAPI renderer; runtime validators via
[openapi-zod-client](https://github.com/astahmer/openapi-zod-client) if you want them.
## Also in the box
- [Contract coverage checking](https://maxanstey-meridian.github.io/rivet/guides/contract-coverage) — `--check` verifies every contract field has an implementation on the declared route and method
- [OpenAPI import](https://maxanstey-meridian.github.io/rivet/guides/openapi-import) — one-shot onboarding for existing APIs: generate C# contracts from a spec, then the C# is the source of truth
- [File endpoints](https://maxanstey-meridian.github.io/rivet/guides/file-uploads), headers as contract concepts, minimal-API hosts, [round-trippable specs](https://maxanstey-meridian.github.io/rivet/guides/openapi-round-trips)
- Stable `RIVnnnn` [diagnostic IDs](https://maxanstey-meridian.github.io/rivet/reference/diagnostics) on every warning — grep or baseline by ID
- A TypeScript-first sibling, [rivet-ts](https://github.com/maxanstey-meridian/rivet-ts) — same pipeline, contracts authored as TS types, Hono runtime
## Documentation
[Getting Started](https://maxanstey-meridian.github.io/rivet/getting-started) ·
[Contracts](https://maxanstey-meridian.github.io/rivet/guides/contracts) ·
[CLI Reference](https://maxanstey-meridian.github.io/rivet/reference/cli) ·
[Type Mapping](https://maxanstey-meridian.github.io/rivet/reference/type-mapping) ·
[Runtime Validation](https://maxanstey-meridian.github.io/rivet/guides/runtime-validation)
(the precise scope of what is and isn't enforced at runtime)
## License
MIT