Summary#
app.Apis publishes your application over HTTP for other systems to call. The guiding idea is that you expose what
you already have rather than write a web layer: you do not author controllers, routes, DTOs or handlers. Two things
can be published — an entity, as a set of CRUD routes, and a function, mapped to a single route. Each API has a
Route prefix and an optional Auth gate.
app.Apis = [
new RestApi("Orders") {
Route = "orders",
Auth = new ApiAuth { ApiKey = true },
Expose = [ new Crud<Order>() { Operations = [CrudOp.Read, CrudOp.Create] } ],
Endpoints = [ new Endpoint(SubmitOrder) { Method = HttpMethod.Post, Path = "/submit" } ],
},
];Signature#
app.Apis = [
new RestApi("Name") { // one entry per published API
Route = "orders", // the URL prefix for every route below
Version = "1.0", // optional — omit and it defaults to v1
Auth = new ApiAuth { ApiKey = true, Bearer = true }, // optional gate
Expose = [ // entities → CRUD routes
new Crud<Order>() { Operations = [CrudOp.Read, CrudOp.Create, CrudOp.Update, CrudOp.Delete] },
],
Endpoints = [ // functions → one route each
new Endpoint(SubmitOrder) { Method = HttpMethod.Post, Path = "/submit", SuccessStatus = 201 },
],
},
];app.Apis is a list — an app may publish several independent APIs, each with its own Route and Auth.
Description#
A RestApi has:
Route— the URL prefix under which its routes live.Version,Deprecated,Sunset(optional) — API lifecycle metadata. Every route lives under a/v{major}/prefix; an omittedVersiondefaults to v1, so the simple case needs no version at all. A multi-major API gives eachRestApiits ownVersion(e.g."1.0","2.0").Auth(optional) — anApiAuth { ApiKey = true, Bearer = true }. Absent, the API is unauthenticated.Expose— a list ofnew Crud<Entity>() { Operations = [CrudOp.…] }. Each entry turns one entity into REST routes;Operationschooses which ofRead/Create/Update/Deleteexist. The entity must be defined in your app — aCrud<Unknown>is a compile error that lists the entities you do define.Endpoints— a list ofnew Endpoint(Function) { Method = HttpMethod.…, Path = "…" }. Each maps one of your functions to a route. The function must be declared — anEndpoint(NoSuchFn)is a compile error that lists your functions.
You write no request parsing, no serialization, and no routing table: the shape of the entity and the signature of the function are the contract.
Refusals map to the right status. When an endpoint's function refuses — it throws a typed exception, or a
declared security policy denies the caller — the response carries the matching 4xx, not a blanket 500, and the client
sees the refusal's own message:
| The function… | Response |
|---|---|
throw new ValidationException("…") | 400 — the request was invalid |
throw new NotFoundException("…") | 404 — the target does not exist / is not visible |
throw new ConflictException("…") | 409 — conflicts with current state |
| is denied by a security / authorization policy | 403 — the caller may not do this |
| hits a genuine, unexpected error | 500 — a generic message; the details stay in the server log, never the response |
So a business rule like "a backup can only be downloaded once it is ready" is a throw new ValidationException(…)
that reaches the caller as a 400 with your wording — not a server crash.
Examples#
A complete app that publishes one entity as read/create CRUD and one function as a POST route:
entity Order {
[Required, MaxLength(200)] string CustomerEmail;
decimal Total;
}
decimal SubmitOrder(decimal total, decimal taxRate) {
return total + total * taxRate;
}
app.Apis = [
new RestApi("Orders") {
Route = "orders",
Expose = [ new Crud<Order>() { Operations = [CrudOp.Read, CrudOp.Create] } ],
Endpoints = [
new Endpoint(SubmitOrder) { Method = HttpMethod.Post, Path = "/submit", SuccessStatus = 201 },
],
},
];Require an API key on every route — declare the secret, then gate the API with Auth:
entity Product { [Required, MaxLength(200)] string Name; decimal Price; }
app.Secrets = [ new Secret("OrdersApiKey") ];
app.Apis = [
new RestApi("Catalog") {
Route = "catalog",
Auth = new ApiAuth { ApiKey = true },
Expose = [ new Crud<Product>() { Operations = [CrudOp.Read] } ],
},
];See also#
- declaring secrets (app.Secrets) —
app.Secrets, where an API key referenced byAuthis declared - [AuthMethod] — a function an unauthenticated visitor may call — signing a user in, for bearer-authenticated routes
- function — the functions an
Endpointpublishes