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:
- New features in specification
- Quality of code generation
- Focus on server-side API development
- Change management
- Cloud-based code generation and publishing
The Fern specification was built to be easy to read and write. Here are some examples.
Here’s how you’d define a simple string-to-string dictionary in Fern:
type: object additionalProperties: type: string
Example: Discriminated union
Here’s how you might define an
Animal union type in Fern:
Animal: discriminant: pet_type union: dog: Dog cat: Cat Dog: properties: breed: DogBreed DogBreed: enum: - Dingo - Husky - Retriever - Shepherd Cat: properties: hunts: boolean
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.
anyOfconcept 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).
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
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.