Skip to content

hyperpolymath/rescript-openapi

Repository files navigation

rescript-openapi

Generate type-safe ReScript clients from OpenAPI specifications.

Why

OpenAPI is the de facto standard for describing REST APIs. This tool generates:

  • Type definitions - Records, variants, aliases from schemas

  • Runtime validators - Using rescript-schema for safe JSON parsing

  • HTTP clients - Pluggable HTTP layer (fetch by default, bring your own)

  • Operation aliases - Multiple ways to call the same endpoint

Installation

cargo install rescript-openapi

Or build from source:

git clone https://github.com/hyperpolymath/rescript-openapi
cd rescript-openapi
cargo build --release

Usage

Generate Client

rescript-openapi generate -i openapi.yaml -o src/api

This generates:

  • ApiTypes.res - All type definitions

  • ApiSchema.res - rescript-schema validators

  • ApiClient.res - HTTP client with fetch

Options

Flag Description Default

-i, --input

Path to OpenAPI spec (JSON/YAML)

Required

-o, --output

Output directory

src/api

-m, --module

Module name prefix

Api

--with-schema

Generate rescript-schema validators

true

--with-client

Generate HTTP client

true

Validate Spec

rescript-openapi validate -i openapi.yaml

Show Info

rescript-openapi info -i openapi.yaml

Generated Code

Types (ApiTypes.res)

/** A user in the system */
type user = {
  id: string,
  @as("full_name") fullName: string,
  email: string,
  role: option<userRole>,
}

type userRole = [
  | #Admin
  | #User
  | #Guest
]

Schema Validators (ApiSchema.res)

module S = RescriptSchema

let userSchema: S.t<user> = S.object(s => ({
  id: s.field("id", S.string),
  fullName: s.field("full_name", S.string),
  email: s.field("email", S.string),
  role: s.fieldOr("role", S.option(userRoleSchema), None),
}: user))

let parseUser = (json: Js.Json.t): user => {
  S.parseJsonOrThrow(json, userSchema)
}

let serializeUser = (value: user): Js.Json.t => {
  S.reverseConvertToJsonOrThrow(value, userSchema)
}

HTTP Client (ApiClient.res)

// Default client using fetch
module Client = Make(FetchClient)

// Usage
let config = makeConfig(~baseUrl="https://api.example.com", ())

let result = await Client.getUser(config, ~id="123", ())
switch result {
| Ok(user) => Console.log(user.fullName)
| Error(err) => Console.error(err.message)
}

Custom HTTP Backend

// Implement the HttpClient signature
module AxiosClient: HttpClient = {
  let request = async (req: httpRequest) => {
    // Use axios, ky, got, or any HTTP library
    let response = await Axios.request({
      method: req.method->methodToString,
      url: req.url,
      headers: req.headers,
      data: req.body,
    })
    Ok(response.data)
  }
}

// Create client with custom HTTP
module Api = Make(AxiosClient)

Mock for Testing

module MockClient: HttpClient = {
  let request = async (req) => {
    // Return mock data
    Ok(%raw(`{"id": "123", "full_name": "Test User"}`))
  }
}

module TestApi = Make(MockClient)

ReScript Dependencies

Add to your rescript.json:

{
  "bs-dependencies": [
    "@rescript/core",
    "rescript-schema",
    "@glennsl/rescript-fetch"
  ]
}

Install with npm:

npm install @rescript/core rescript-schema @glennsl/rescript-fetch

Roadmap

  • ❏ OpenAPI 3.1 support

  • oneOf/anyOf discriminated unions

  • ❏ File upload/download

  • ❏ Streaming responses

  • ❏ WebSocket support

  • ❏ GraphQL schema generation

Part of rescript-full-stack

This tool is part of the ReScript Full Stack ecosystem.

License

AGPL-3.0-or-later

About

Generate type-safe ReScript clients from OpenAPI specifications

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •