spac

spac

TypeScript DSL for authoring OpenAPI 3.1+ specs

spac is a TypeScript DSL for authoring OpenAPI 3.1+ specs. You write TypeScript — OpenAPI is the output.

import { Api, named } from '@spec-spac/spac'
import { Type } from '@sinclair/typebox'
const Pet = named('Pet', Type.Object({
  id: Type.String(),
  name: Type.String(),
}))
const api = new Api('3.1', 'Petstore')
api.group('/pets', g => {
  g.get('/').response(Type.Array(Pet)).tag('pets')
  g.post('/').body(Pet).response(Pet).tag('pets')
})
export default api
openapi: 3.1.2
info:
  title: Petstore
  version: 1.0.0
jsonSchemaDialect: https://json-schema.org/draft/2020-12/schema
paths:
  /pets:
    get:
      tags:
        - pets
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Pet"
    post:
      tags:
        - pets
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Pet"
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Pet"
components:
  schemas:
    Pet:
      type: object
      required:
        - id
        - name
      properties:
        id:
          type: string
        name:
          type: string

Install

npm install @spec-spac/spac @sinclair/typebox
pnpm add @spec-spac/spac @sinclair/typebox
bun add @spec-spac/spac @sinclair/typebox

Packages

PackageWhat it does
@spec-spac/spacCore library — define routes, groups, schemas, emit OpenAPI 3.1 JSON/YAML with source maps
@spec-spac/from-openapiCLI + library — reverse-generate spac TypeScript from an existing OpenAPI spec
@spec-spac/from-openapi-biomeBiome formatter plugin for the code generator
@spec-spac/from-openapi-prettierPrettier formatter plugin for the code generator

Why spac?

  • TypeBox schemas directly — no wrapper DSL, full JSON Schema type inference
  • Named schemas auto-hoist to components.schemas as $ref
  • Group inheritance — tags and security cascade from groups to routes
  • Macro system — reusable route/group/api transforms
  • Source maps — trace emitted YAML back to TypeScript source lines
  • Reverse generator — already have an OpenAPI spec? Generate spac code from it with spac-from-openapi

Quick start

Create api.ts:

import { Api, named } from '@spec-spac/spac'
import { Type } from '@sinclair/typebox'

const User = named('User', Type.Object({
  id: Type.String(),
  name: Type.String(),
  email: Type.String({ format: 'email' }),
}))

const api = new Api('3.1', 'My API', { version: '1.0.0' })

api.group('/users', g => {
  g.tag('users')
  g.get('/').response(Type.Array(User)).summary('List users')
  g.post('/').body(User).response(User).summary('Create user')
})

console.log(JSON.stringify(api.emit(), null, 2))

Run it:

npx tsx api.ts > openapi.json
pnpm dlx tsx api.ts > openapi.json
bunx tsx api.ts > openapi.json

That's it — openapi.json is a valid OpenAPI 3.1 document.

spac-from-openapi CLI

Already have an OpenAPI spec? Generate spac TypeScript from it:

npx spac-from-openapi petstore.json --out ./generated
pnpm dlx spac-from-openapi petstore.json --out ./generated
bunx spac-from-openapi petstore.json --out ./generated

Use --strip to clean up deeply nested paths:

spac-from-openapi cloudflare.json --out ./generated \
  --strip '/accounts/{account_id}'

See the full CLI reference for all options.

Documentation

On this page