Skip to content
ThomasparsleyPublic

About

CLI tool that transforms a GraphQL persisted operations map and schema into a fully typed OpenAPI v3 YAML specification. Designed for teams running persisted-only GraphQL APIs with RPC-style endpoints.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

peroapi

Generate an OpenAPI v3 spec from a persisted-only GraphQL API, so external systems can use it without a second REST API.

Why

You already have a GraphQL API locked to persisted operations and used internally. Building and maintaining a separate REST API for external consumers costs time and money. Each persisted operation has a fixed name, variables and response shape, so it maps directly to an HTTP endpoint. peroapi turns your schema and persisted operations into a typed OpenAPI spec that works with codegen, gateways, Postman and docs portals.

peroapi only generates the spec. It runs no server, so an API gateway or thin handler must route requests to your persisted operations.

Public operations

Never publish internal operations. They change with the UI, and external systems can't follow. Instead:

  • Separate operations: write dedicated public queries and mutations in their own persisted operations file (e.g. gql-persisted.public.json).
  • Stable contract: treat a public operation like a REST endpoint. Only make additive changes (new optional variables, new response fields). For breaking changes, add a new operation (e.g. GetUserV2) and deprecate the old one.
  • Public set only: generate the spec from the public file, so internal operations never appear in it.
peroapi generate \
  --schema schema.graphql \
  --operations gql-persisted.public.json \
  --output openapi.public.yaml

Each operation becomes a route ({base-path}/{OperationName}), with variables and response selection sets resolved against the schema into typed OpenAPI schemas.

Installation

go install github.com/Thomasparsley/peroapi@latest

Or build from source:

git clone https://github.com/Thomasparsley/peroapi.git
cd peroapi
go build -o peroapi .

Usage

peroapi generate --schema <schema.graphql> --operations <gql-persisted.json> [flags]

Flags

Flag Short Default Description
--schema -s — Path to the GraphQL schema (required)
--operations -o — Path to the persisted operations map JSON (required)
--output (prompt) Path to write the OpenAPI YAML. If omitted, you're asked whether to print to stdout
--method-convention rest HTTP method strategy: rest, post, or get
--base-path /api/graphql Path prefix for every generated route
--title Generated API Value for OpenAPI info.title
--version 1.0.0 Value for OpenAPI info.version

Method conventions

  • rest (default) — queries map to GET, mutations map to POST.
  • post — every operation maps to POST.
  • get — every operation maps to GET.

For GET routes, operation variables become individual query parameters. For POST routes, variables are packed into a JSON request body under a variables object.

Input formats

Persisted operations map (gql-persisted.json) — a flat object mapping operation names to their GraphQL document strings:

{
  "GetUser": "query GetUser($id: ID!) { user(id: $id) { id name email } }",
  "CreateUser": "mutation CreateUser($input: CreateUserInput!) { createUser(input: $input) { id name } }"
}

Schema (schema.graphql) — a standard GraphQL SDL file defining the types referenced by your operations:

input CreateUserInput {
  name: String!
  email: String!
}

type User {
  id: ID!
  name: String!
  email: String!
}

type Query {
  user(id: ID!): User
}

type Mutation {
  createUser(input: CreateUserInput!): User!
}

Custom scalars

Built-in scalars map to their OpenAPI equivalents. Custom scalars the schema declares are recognized by name:

Scalar OpenAPI schema
Byte integer/int32, -128 to 127
UnsignedByte integer/int32, 0 to 255
Short integer/int32, -32768 to 32767
UnsignedShort integer/int32, 0 to 65535
UnsignedInt integer/int64, 0 to 4294967295
Long integer/int64
UnsignedLong integer, minimum 0 (the 2^64-1 maximum doesn't fit int64, so it is not declared)
LocalDate string/date

Any other custom scalar becomes a plain string with an x-graphql-scalar: <Name> extension.

Names are matched literally and follow Hot Chocolate's conventions, where Byte is signed and UnsignedByte is unsigned. If your server gives a name different semantics, the generated range for that scalar is wrong.

Examples

Generate a spec using the default REST convention and write it to a file:

peroapi generate \
  --schema schema.graphql \
  --operations gql-persisted.json \
  --output openapi.yaml

Generate with every operation forced to POST. With no --output, peroapi prompts whether to print to stdout:

peroapi generate -s schema.graphql -o gql-persisted.json --method-convention post
# No output file specified. Print to stdout? [Y/n]:

Customize the route prefix and API metadata:

peroapi generate \
  -s schema.graphql \
  -o gql-persisted.json \
  --base-path /v1/rpc \
  --title "Orders API" \
  --version 2.3.0 \
  --output orders-openapi.yaml

Example output

With the rest convention, a query like GetUser becomes a GET route (variables as query parameters) and a mutation like CreateUser becomes a POST route (variables in the request body):

openapi: 3.0.3
info:
    title: Generated API
    version: 1.0.0
paths:
    /api/graphql/GetUser:
        get:
            operationId: GetUser
            parameters:
                - name: id
                  in: query
                  required: true
                  schema:
                    type: string
            responses:
                "200":
                    description: Successful response
                    # ... typed data + errors object
    /api/graphql/CreateUser:
        post:
            operationId: CreateUser
            requestBody:
                required: true
                content:
                    application/json:
                        schema:
                            type: object
                            properties:
                                variables:
                                    type: object
                                    properties:
                                        input:
                                            $ref: '#/components/schemas/CreateUserInput'
            responses:
                "200":
                    description: Successful response
                    # ... typed data + errors object

License

See LICENSE.

About

CLI tool that transforms a GraphQL persisted operations map and schema into a fully typed OpenAPI v3 YAML specification. Designed for teams running persisted-only GraphQL APIs with RPC-style endpoints.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages