We built Fern from the ground up to address our dissatisfaction with OpenAPI.

Despite being a different format for describing APIs, you are never locked in to Fern. It’s easy to convert your Fern Definition to OpenAPI.

TL;DR: we differ from OpenAPI in these areas:

Simplicity

The Fern specification was built to be easy to read and write. Here are some examples.

Example: Map

Here’s how you’d define a simple string-to-string dictionary in Fern:

Fern example
map<string, string>

In OpenAPI:

OpenAPI example
type: object
additionalProperties:
  type: string

Example: Discriminated union

Here’s how you might define an Animal union type in Fern:

Fern example
Animal:
  discriminant: pet_type
  union:
    dog: Dog
    cat: Cat

Dog:
  properties:
    breed: DogBreed

DogBreed:
  enum:
    - Dingo
    - Husky
    - Retriever
    - Shepherd

Cat:
  properties:
    hunts: boolean

In OpenAPI:

OpenAPI example
Animal:
  oneOf:
  - "$ref": "#/components/schemas/Cat"
  - "$ref": "#/components/schemas/Dog"
  discriminator:
    propertyName: pet_type
BaseAnimal:
  type: object
  required:
  - pet_type
  properties:
    pet_type:
      type: string
  discriminator:
    propertyName: pet_type
Dog:
  allOf:
  - "$ref": "#/components/schemas/BaseAnimal"
  - type: object
    properties:
      bark:
        type: boolean
      breed:
        type: string
        enum:
        - Dingo
        - Husky
        - Retriever
        - Shepherd
Cat:
  allOf:
  - "$ref": "#/components/schemas/BaseAnimal"
  - type: object
    properties:
      hunts:
        type: boolean

New features in specification

As we continue to build Fern, our ability to control the format is invaluable. For example, we’re adding support for asynchronous / bi-directional protocols (e.g. websockets) which is not possible in OpenAPI.

Quality of code generation

We built Fern from first principles to ensure that we always generate idimoatic code. There are a number of footguns in OpenAPI: if you use them, it’s impossible to generate high-quality code. Here are examples:

  • Using inline (anonymous) types in an OpenAPI spec makes it impossible to generate idiomatic code, as most languages do not support anonymous type declarations.
  • It’s easy to define non-discriminated unions in OpenAPI, which makes for tricky-to-use SDKs. In many languages, it’s difficult or impossible to deserialize non-discriminated unions correctly.
  • Common errors cannot be reused in OpenAPI. This results in duplicative generated code that doesn’t feel handwritten.
  • OpenAPI’s anyOf concept is impossible to represent in most programming languages in sub-exponential time.

Pitfalls like these are why it’s common for OpenAPI-generated code to not compile.

Beyond the format, we’ve built Fern based on best practices in compiling. This includes:

  • Semantic validation (e.g. disallowing references to types that haven’t been defined)
  • Building the compiler to be modular, as we have multiple independent outputs (e.g. TypeScript SDK, Postman Collection).
  • Producing an intermediate representation so that different generators don’t have to implement duplicative logic. Beyond saving time, this reduces errors and increases consistency among outputs.
  • Using AST representations, rather than templates, to enable more complex and idiomatic code generation.

Focus on server-side API development

OpenAPI is focused primarily on documentation and SDK generation and is not very helpful for backend API development. In comparison, we’ve focused heavily on server-side integration, as that’s where most of the API development process occurs! In particular, we:

  • Auto-generate the types (e.g. Pydantic models for FastAPI)
  • Auto-generate the networking logic (e.g. FastAPI routes)
  • Auto-generate exceptions that you can throw. Fern handles converting to the correct HTTP status code.
  • Auto-generate server interfaces for you to implement your business logic. This ensures you implement your API correctly. For example, if you return the wrong type for an endpoint, you’ll get a compile error.

All server code that Fern generates is intended to live in a segmented generated/ directory on your backend, which you can import into your code. This directory can be checked into git and can easily be regenerated. In comparison, the OpenAPI generators output server stubs, which overwrite your server implementation (see this issue that’s been open since 2018).

Change management

An important difference between Fern and OpenAPI is our versioning and change management strategy.

With OpenAPI, you’re beholden to their infamous feature matrices; if you want to use a feature, you first need to ensure that all your generators support it.

In comparison, we’ve built a custom migration framework for our intermediate representation so that most new features are implicitly supported by older generators. And when a migration isn’t possible, you’ll get a clear error from our compiler of how you can remediate.

Cloud-based code generation and publishing

Fern runs your code generation in the cloud by default. This improves reliability as we run the generators in consistent, containerized environments. You run fern generate, and it just works. You don’t need Java installed on your computer to generate a Java SDK.

Fern handles publishing too. We don’t just spit out a bunch of code and say, “good luck.” Fern can publish directly to registries (e.g. npm, Maven) and to GitHub repos.