resAPI
Type-safe HTTP contracts for ReScript
Define your API once. Share types and validation between clients and servers, on Hono, Express or Cloudflare Workers.
1. Describe the endpoint
A contract names the route, the params, the response and the errors the endpoint can return. Clients and servers both import it.
AccountApi.res
open ResApi @schema type params = {id: string} @schema type account = {id: string, name: string, balance: float} @schema type accountError = | @as("not_found") NotFound let getAccount = Endpoint.get( "/accounts/:id", ~params=SuryCodec.make(paramsSchema), ~response=SuryCodec.make(accountSchema), )->Endpoint.withErrors(SuryCodec.make(accountErrorSchema), ~status=error => switch error { | NotFound => 404 } )
2. Implement it
The handler receives decoded, validated params. Returning a field the contract doesn't have, or an error it doesn't declare, is a compile error.
App.res
open ResApiHono let app: Router.t<unit> = Router.make() ->Router.register( AccountApi.getAccount->EndpointHandler.make(async (_ctx, {params}) => switch accounts->Dict.get(params.id) { | Some(account) => Ok(account) | None => Error(NotFound) } ), ) // A router is a Hono app, so it is already a Cloudflare Worker. let default = app
3. Call it
The client builds the URL, validates the response, and returns every outcome as a typed case.
AccountClient.res
switch await client->Client.call(AccountApi.getAccount, ~params={id: id}) { | Ok(account) => account.name | Error(Application({error: NotFound})) => "no such account" | Error(_) => "request failed" }
Why resAPI
- One definition
Server handlers and client calls take their types from the same contract value, with no generated code.
- Validated at both ends
Requests are decoded before your handler runs. Responses are decoded before your code sees them.
- Typed application errors
Declare
NotFoundorAccessDeniedwith their statuses. They never mix with network or validation failures. - Native frameworks
A router is a Hono or Express app, so their middleware works as is.
Packages
| @resapi/core | Contracts, codecs and the typed client. Depends only on Fetch. |
| @resapi/hono | Hono adapter, for Node and Cloudflare Workers. |
| @resapi/express | Express 5 adapter. |
| @resapi/sury | Sury schemas as codecs. Hand-written codecs work too. |
0.1.0 is a preview: the API may change in 0.x releases. See the changelog for tested versions and known limitations.