Skip to navigation

Types in Fern Definition

View as Markdown

Fern Definition isn’t recommended for new customers and Fern isn’t accepting feature requests for this format. It remains supported for existing users.

Types describe the data model of your API.

Built-in types

TypeDescription
stringBasic string type
integerInteger number type
longLong integer type
doubleDouble precision floating point
booleanBoolean true/false
datetimeAn RFC 3339, section 5.6 datetime. For example, 2017-07-21T17:32:28Z
dateAn RFC 3339, section 5.6 date (YYYY-MM-DD). For example, 2017-07-21
uuidUUID identifier
base64Base64 encoded data
listAn ordered collection that allows duplicates, e.g., list<string>
setAn unordered collection with unique elements, e.g., set<string>
mapKey-value mapping, e.g., map<string, integer>
optionalOptional value, e.g., optional<string>
literalLiteral value, e.g., literal<"Plants">
fileFile upload type, e.g., file uploads
unknownRepresents arbitrary JSON

Custom types

Creating your own types is easy in Fern!

Objects

The most common custom types are objects.

In Fern, you use the "properties" key to create an object:

types:
Person:
properties:
name: string
address: Address
Address:
properties:
line1: string
line2: optional<string>
city: string
state: string
zip: string
country: literal<"USA">

These represent JSON objects:

{
"name": "Alice",
"address": {
"line1": "123 Happy Lane",
"city": "New York",
"state": "NY",
"zip": "10001",
"country": "USA"
}
}

You can also use extends to compose objects:

types:
Pet:
properties:
name: string
Dog:
extends: Pet
properties:
breed: string

You can extend multiple objects:

types:
GoldenRetriever:
extends:
- Dog
- Pet
properties:
isGoodBoy: boolean

Aliases

An Alias type is a renaming of an existing type. This is usually done for clarity.

types:
# UserId is an alias of string
UserId: string
User:
properties:
id: UserId
name: string

Enums

An enum represents a string with a set of allowed values.

In Fern, you use the "enum" key to create an enum:

types:
WeatherReport:
enum:
- SUNNY
- CLOUDY
- RAINING
- SNOWING

Enum names are restricted to A-Z, a-z, 0-9, and _ to ensure that generated code can compile across all of the languages that Fern can output. If you have an enum that doesn’t follow this convention, you can use the "name" key to specify a custom name:

types:
Operator:
enum:
- name: LESS_THAN # <--- the name that will be used in SDKs
value: '<' # <--- the value that will be serialized
- name: GREATER_THAN
value: '>'
- name: NOT_EQUAL
value: '!='

Discriminated unions

Fern supports tagged unions (a.k.a. discriminated unions). Unions are useful for polymorphism. This is similar to the oneOf concept in OpenAPI.

In Fern, you use the "union" key to create an union:

types:
Animal:
union:
dog: Dog
cat: Cat
Dog:
properties:
likesToWoof: boolean
Cat:
properties:
likesToMeow: boolean

In JSON, unions have a discriminant property to differentiate between different members of the union. By default, Fern uses "type" as the discriminant property:

{
"type": "dog",
"likesToWoof": true
}

You can customize the discriminant property using the “discriminant” key:

types:
Animal:
discriminant: animalType
union:
dog: Dog
cat: Cat
Dog:
properties:
likesToWoof: boolean
Cat:
properties:
likesToMeow: boolean

This corresponds to a JSON object like this:

{
"animalType": "dog",
"likesToWoof": true
}

Undiscriminated unions

Undiscriminated unions are similar to discriminated unions, however you don’t need to define an explicit discriminant property.

MyUnion:
discriminated: false
union:
- string
- integer

Generics

Fern supports shallow generic objects, to minimize code duplication. You can define a generic for reuse like so:

MySpecialMapItem<Key, Value>:
properties:
key: Key,
value: Value,
diagnostics: string

Now, you can instantiate generic types as a type alias:

StringIntegerMapItem:
type: Response<string, number>
StringStringMapItem:
type: Response<string, string>

You can now freely use this type as if it were any other type! Note, generated code will not use generics. The above example will be generated in typescript as:

type StringIntegerMapItem = {
key: string,
value: number,
diagnostics: string
}
type StringStringMapItem = {
key: string,
value: string,
diagnostics: string
}

Documenting types

You can add documentation for types. These docs are passed into the compiler, and are incredibly useful in the generated outputs (e.g., docstrings in SDKs).

Fern Definition
types:
Person:
docs: A person represents a human being
properties:
name: string
age:
docs: age in years
type: integer
Generated TypeScript SDK from Fern Definition
/**
* A person represents a human being
*/
interface Person {
name: string;
// age in years
age: number;
}

Validating types

You can add validation constraints to your types (both aliases and references) to ensure data integrity. These validation constraints exist in your API definition and are enforced by the server, but the generated client SDKs don’t include validation logic.

Fern Definition
types:
Person:
docs: A person represents a human being
properties:
name:
docs: The person's full name
type: string
validation:
minLength: 2
maxLength: 100
pattern: "^[A-Za-z ]+$"
age:
docs: Age in years
type: integer
validation:
min: 0
max: 150

String types support several validation constraints.

types:
Word:
type: string
validation:
minLength: 2
maxLength: 26
User:
properties:
email:
type: string
validation:
format: email
maxLength: 254
username:
type: string
validation:
minLength: 3
maxLength: 20
pattern: "^[a-zA-Z0-9_]+$"
minLength
integer

Minimum number of characters required

maxLength
integer

Maximum number of characters allowed

pattern
string

Regular expression pattern that the string must match

format
string

String format specification (e.g., “email”, “uri”, “date-time”)

Number types (including integer, long, and double) support several validation constraints.

types:
Age:
type: integer
validation:
min: 0
max: 150
Product:
properties:
name: string
price:
type: double
validation:
min: 0
exclusiveMin: true
multipleOf: 0.01
quantity:
type: integer
validation:
min: 1
max: 1000
min
number

Minimum value (inclusive by default)

max
number

Maximum value (inclusive by default)

exclusiveMin
boolean

When true, the minimum value is exclusive (value must be greater than min)

exclusiveMax
boolean

When true, the maximum value is exclusive (value must be less than max)

multipleOf
number

Value must be a multiple of this number