Library / SDK
pb33f/libopenapi avatar
pb33f/libopenapi

libopenapi: an OpenAPI parser that keeps the specification as a live model

libopenapi is a fully featured, high performance OpenAPI 3.2, 3.1, 3.0, Overlays and Arazzo parser, library, validator and toolkit for go applications.

882 stars125 forksGoMIT

At a glance

What is it?
libopenapi is a Go library for OpenAPI 3, 3.1 and 3.2 documents plus Overlays and Arazzo, and its distinguishing choice is that it hands you a mutable model with an index, a resolver and a rolodex rather than a plain struct tree. That makes diffing, bundling and code generation possible on the same parse you already did.
Who is it for?
Use libopenapi when your Go service has to reason about a specification rather than just read it, because the indexed model with a resolver and a rolodex is what makes diffing, bundling, mocking and code generation possible from one parse. Do not reach for it if a plain struct unmarshalling would do, since the index and the resolution machinery cost you memory and complexity for capabilities you would not use.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Three specification versions, two newer companion formats

The description calls libopenapi a fully featured, high performance parser, library, validator and toolkit for Go applications, and the version coverage is the first thing to check. It has full support for OpenAPI 3, 3.1 and 3.2, and the README states it can handle the largest and most complex specifications you can think of. Two further formats are supported as well: Overlays, which apply modifications on top of an existing document, and Arazzo, the workflow description format for multi-step API calls. The Swagger side of the family is also covered by the documentation set, which has a dedicated page for using Swagger alongside the OpenAPI pages. The licence field on the repository says MIT, and the releases are frequent: v0.41.6 on 2026-10-10, v0.41.5 and v0.41.4 on 2026-10-07, with the last push to the repository on 2026-10-19.

A model you can walk, not just unmarshal

The quick start shows the shape of the API. You build a document with libopenapi.NewDocument from the raw bytes, then call BuildV3Model on it to get the model, and from there you work with docModel.Model.Components.Schemas. The interesting method in the sample is FromOldest, which is what the comment about the following failing after the first iteration refers to: iterating a map of schemas while mutating it is a classic Go mistake, and the library offers an ordered traversal that avoids it. The reason a plain unmarshal is not enough is that a specification is not a tree of values. It has references between parts, possibly circular ones, possibly across files, and a tool that only gives you the raw structure has to leave all of that to you. The model is where those relationships live, and the rest of the library is built on top of it.

Index, resolver and rolodex are the three pieces to learn

Three named subsystems carry most of the capability, and the documentation has a page for each. The index is the lookup structure over the document, so a reference by name or by pointer becomes a lookup rather than a scan. The resolver is what turns a reference into the thing it points at. The rolodex is the broader machinery for managing a document and everything it depends on, including files pulled in from elsewhere. Circular references have their own documentation page, which tells you the design has to cope with them explicitly rather than treating them as an error. These three are also why the library can bundle specs, another documented topic: bundling is the operation of resolving every external reference and writing out one self-contained document, which is only possible because the resolver and the rolodex already know how to fetch and interpret what a reference means.

Diffing, mutation and mocking from the same model

Several capabilities that usually need separate tools are documented as operations on the model. There is a What Changed page describing a diff engine, so you can compare two versions of a specification and find what moved, which is what a breaking-change check in a pull request pipeline needs. There is a page on modifying and mutating the OpenAPI model, so the model is writable rather than read-only. There is a mocking and creating examples page, which covers producing example payloads from the schemas in the document. And a validation page, alongside a note that request, response, parameter and schema validation now lives in the separate libopenapi-validator module, which the README points to rather than folding into this package. The pattern to notice is that these are all operations over one parsed model, so a tool that parses once can validate, diff and mock without re-parsing the document for each task.

Running the quick start end to end

The tutorial starts by fetching a spec from the OpenAPI Specification repository and then adding the library to a Go module.

bash
curl https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/_archive_/schemas/v3.0/pass/petstore.yaml > petstorev3.json
bash
go get github.com/pb33f/libopenapi

The program itself is a short main.go that reads the file, builds the document and the V3 model, and walks the component schemas printing how many properties each one has.

go
package main

import (
	"fmt"
	"os"
	"github.com/pb33f/libopenapi"
)

func main() {
	petstore, _ := os.ReadFile("petstorev3.json")
	document, err := libopenapi.NewDocument(petstore)
	if err != nil {
		panic(fmt.Sprintf("cannot create new document: %e", err))
	}
	docModel, err := document.BuildV3Model()
	if err != nil {
		panic(fmt.Sprintf("cannot create v3 model from document: %e", err))
	}
}

Run it and the output lists the schema names with their property counts, Pet with three properties and Error with two.

Generators for TypeScript alongside the Go one

Code generation is a documented feature, with pages for both generating code and parsing code. The interesting recent addition is the TypeScript generator, which renders the component schemas of an OpenAPI document as a type-only TypeScript module, built from the same model as the Go generator.

go
file, err := typescript.RenderSchemas(docModel.Model.Components.Schemas)
os.WriteFile("api.gen.ts", file.Source, 0o644)

The point of using the same model is consistency. If the Go and the TypeScript generators each had their own walk over the document, they would disagree about edge cases, and the type definitions would not match what the server actually validates. Sharing the model means the mapping decisions are made once. The generator directory has its own README describing how each schema shape maps to TypeScript, which is the document to read before trusting the output for a complex spec.

A small dependency list for a large surface

The module file is worth reading because it shows what the library leans on. It requires go 1.26.0, and the direct dependencies are few: lucasjones/reggen for generating example values from schemas, pb33f/go-yaml as the YAML parser, pb33f/jsonpath for JSONPath expressions over the document, pb33f/ordered-map for order-preserving maps, pb33f/testify for assertions, and golang.org/x/sync for concurrency primitives. Two indirect dependencies come in through the ordered map. Two of these are first-party forks maintained by the same author, which is a pattern that shows up when an upstream package is missing a feature the library needs. The repository layout matches the feature list closely: an arazzo.go and arazzo directory for the workflow format, overlay.go and an overlay directory for overlays, bundler for the bundling operation, index for the lookup structure, datamodel and internal for the model, renderer for output formats, what-changed for the diff engine, json for the JSON handling, orderedmap for the ordered map type, and a generator directory for the code generators.

Where libopenapi is the wrong choice

Four limits apply. It is a Go library, so a team on another language has to look elsewhere or run a service around it. It parses rather than serves, so if all you need is a client generated from a specification, a code generator with no runtime dependency is less to carry, and the generator directory here exists precisely because many people want that instead. The index, the resolver and the rolodex cost memory and complexity, and a small application that just needs a couple of fields out of a spec does not need them. And the API churns: the project ships a MIGRATING.md document titled about breaking API changes by version, which tells you that minor releases can move things and that you should read it before upgrading a pinned dependency. The project also leans on sponsorships for continuity, and the README asks companies using it to consider supporting the project.

Editorial conclusion

Use libopenapi when your Go service has to reason about a specification rather than just read it, because the indexed model with a resolver and a rolodex is what makes diffing, bundling, mocking and code generation possible from one parse. Do not reach for it if a plain struct unmarshalling would do, since the index and the resolution machinery cost you memory and complexity for capabilities you would not use. Verify first that the specification version your clients send is one of the three it supports, that you are comfortable with the model API changing across minor versions because the project ships a breaking-change migration document by version, and that the separate validator module is where you should look for request and response validation rather than assuming it is in this package.

Frequently asked questions

Which OpenAPI versions does libopenapi support?

It has full support for OpenAPI 3, 3.1 and 3.2, and Overlays and Arazzo are fully supported as well. The documentation also covers using Swagger alongside OpenAPI.

How do I parse an OpenAPI document in Go with libopenapi?

Read the bytes, create a document with libopenapi.NewDocument, then call BuildV3Model on it and work with the model. The quick start in the README uses the petstore example and walks the component schemas with FromOldest.

What are the index, resolver and rolodex in libopenapi?

They are the three subsystems the documentation gives their own pages. The index is the lookup structure over a document, the resolver turns a reference into the thing it points at, and the rolodex manages the document together with everything it depends on, which is what makes bundling and circular reference handling possible.

Where does request and response validation live in libopenapi?

The README points to a separate libopenapi-validator module for validating requests, responses, parameters or schemas, so validation of live traffic is not part of this package.

Can libopenapi generate TypeScript types from a specification?

Yes. The generator/typescript package renders the component schemas as a type-only TypeScript module using the same model as the Go generator, with typescript.RenderSchemas returning the file source, and its own README describing how each schema shape maps to TypeScript.

Official sources

  1. License: MIT
  2. pb33f/libopenapi on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/pb33f-libopenapi.svg)](https://hysenlabs.com/projects/pb33f-libopenapi)

Was this useful?