Fern Definition

Comparison with OpenAPI

We built the Fern Definition because the OpenAPI Specification can be difficult to use. Here’s how we’re different.

Despite being a different format for describing APIs, you are never locked in to using the Fern Definition. It’s a simple command to convert a Fern Definition to an OpenAPI spec.

The Fern Definition differs from the OpenAPI spec in these areas:

Simplicity

The Fern Definition 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 a Fern Definition:

Fern example
1map<string, string>

In OpenAPI:

OpenAPI example
1type: object
2additionalProperties:
3 type: string

Example: Discriminated union

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

Fern example
1Animal:
2 discriminant: pet_type
3 union:
4 dog: Dog
5 cat: Cat
6
7Dog:
8 properties:
9 breed: DogBreed
10
11DogBreed:
12 enum:
13 - Dingo
14 - Husky
15 - Retriever
16 - Shepherd
17
18Cat:
19 properties:
20 hunts: boolean

In an OpenAPI spec:

OpenAPI example
1Animal:
2 oneOf:
3 - "$ref": "#/components/schemas/Cat"
4 - "$ref": "#/components/schemas/Dog"
5 discriminator:
6 propertyName: pet_type
7BaseAnimal:
8 type: object
9 required:
10 - pet_type
11 properties:
12 pet_type:
13 type: string
14 discriminator:
15 propertyName: pet_type
16Dog:
17 allOf:
18 - "$ref": "#/components/schemas/BaseAnimal"
19 - type: object
20 properties:
21 bark:
22 type: boolean
23 breed:
24 type: string
25 enum:
26 - Dingo
27 - Husky
28 - Retriever
29 - Shepherd
30Cat:
31 allOf:
32 - "$ref": "#/components/schemas/BaseAnimal"
33 - type: object
34 properties:
35 hunts:
36 type: boolean

New features in Definition

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 idiomatic 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 abstract syntax tree (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.