oaspec

CI Integration Tests

oaspec does not cover the full OpenAPI 3.x specification. Support is expanded incrementally.

Generate Gleam code from OpenAPI 3.x specifications.

Install

From GitHub Release

Download the oaspec escript binary from the Releases page. Requires Erlang/OTP 27+.

curl -fSL -o oaspec https://github.com/nao1215/oaspec/releases/latest/download/oaspec
chmod +x oaspec
sudo mv oaspec /usr/local/bin/

From source

Requires Gleam 1.15+, Erlang/OTP 27+, and rebar3.

git clone https://github.com/nao1215/oaspec.git
cd oaspec
gleam deps download
gleam run -m gleescript    # produces ./oaspec escript binary
sudo mv oaspec /usr/local/bin/

Usage

1. Create a config file

oaspec init

This creates oaspec.yaml with a commented template. Edit it for your project:

input: openapi.yaml
package: my_api
output:
  dir: ./gen          # base directory (default: ./gen)

Generated code is placed at <dir>/<package> (server) and <dir>_client/<package> (client). Both directory basenames must match package so that Gleam imports resolve correctly. Copy or symlink the output into src/ to use it.

FieldRequiredDefaultDescription
inputyes-Path to OpenAPI 3.x spec (YAML or JSON)
packagenoapiGleam module namespace prefix
modenobothserver, client, or both
output.dirno./genBase output directory
output.serverno<dir>/<package>Server code output path
output.clientno<dir>_client/<package>Client code output path

The directory basename must match package so that import my_api/types resolves. The CLI --output flag works the same as output.dir. A mismatch is an early error.

2. Run the generator

oaspec generate --config=oaspec.yaml

Options:

--config=<path>   Path to config file (default: ./oaspec.yaml)
--mode=<mode>     server, client, or both (default: both)
--output=<path>   Override output base directory

You can also run via gleam run -- generate --config=oaspec.yaml.

3. Generated output

gen/my_api/                 # server (package = "my_api")
  types.gleam               # Domain model types
  request_types.gleam       # Request parameter types
  response_types.gleam      # Response types (tagged unions by status code)
  decode.gleam              # JSON decoders
  encode.gleam              # JSON encoders
  middleware.gleam           # Middleware types and utilities
  handlers.gleam            # Handler stubs (TODO placeholders)
  router.gleam              # Route dispatcher skeleton

gen_client/my_api/          # client
  types.gleam
  decode.gleam
  encode.gleam
  middleware.gleam
  client.gleam              # HTTP client functions
  request_types.gleam
  response_types.gleam

Generated code examples

Given a Petstore OpenAPI spec:

Types

/// A pet in the store
pub type Pet {
  Pet(
    id: Int,
    name: String,
    status: PetStatus,
    tag: Option(String)
  )
}

pub type PetStatus {
  PetStatusAvailable
  PetStatusPending
  PetStatusSold
}

Server handlers

pub fn list_pets(req: request_types.ListPetsRequest) -> response_types.ListPetsResponse {
  let _ = req
  // TODO: Implement list_pets
  todo
}

Client

pub fn create_pet(config: ClientConfig, body: types.CreatePetRequest)
  -> Result(response_types.CreatePetResponse, ClientError) {
  // ...
}

Middleware

pub type Handler(req, res) =
  fn(req) -> Result(res, MiddlewareError)

pub type Middleware(req, res) =
  fn(Handler(req, res)) -> Handler(req, res)

pub fn compose(first: Middleware(req, res), second: Middleware(req, res)) -> Middleware(req, res)
pub fn apply(middlewares: List(Middleware(req, res)), handler: Handler(req, res)) -> Handler(req, res)
pub fn retry(max_retries: Int) -> Middleware(req, res)

OpenAPI support

Supported

Unsupported (exits with error)

These are detected before code generation. The generator prints an error and exits non-zero.

Not yet supported

Schema-to-type mapping

OpenAPI typeGleam type
stringString
integerInt
numberFloat
booleanBool
arrayList(T)
objectCustom type
enumCustom type with variants
nullableOption(T)
allOfMerged custom type
oneOf/anyOf ($ref variants)Sum type

Development

This project uses mise for tool versions and just as a task runner.

mise install          # install Gleam, Erlang, rebar3
just check            # format check, typecheck, build, unit tests
just shellspec        # CLI integration tests (ShellSpec)
just integration      # generated code compile + roundtrip tests

Test structure

CommandToolWhat it tests
just testgleeunitParser, validator, naming, config, collision detection
just shellspecShellSpecCLI behaviour, file generation, content, unsupported feature detection
just integrationgleeunitGenerated code compiles, types/decoders/encoders/handlers/middleware work

License

MIT

Search Document