Fern Definition

The api.yml configuration file

A fern/ folder has a special file called api.yml, which includes all the API-wide configuration.

1fern/
2├─ fern.config.json
3├─ generators.yml
4└─ definition/
5 ├─ api.yml # <---
6 ├─ pet.yml
7 ├─ store.yml
8 └─ user.yml

API name

This name is used to uniquely identify your API in your organization. If you just have one API, then api is a sufficient name.

api.yml
1name: api

API description

You can define a top level API description. This description will come through in the OpenAPI spec and Postman collection.

api.yml
1name: api
2docs: | # <---
3 ## Header
4 This API provides access to...

API version

You can define your API version scheme, such as a X-API-Version header. The supported versions and default value are specified like so:

api.yml
1version:
2 header: X-API-Version
3 default: "2.0.0"
4 values:
5 - "1.0.0"
6 - "2.0.0"
7 - "latest"

Authentication

You can specify the authentication scheme used by your API.

Out of the box, Fern supports Bearer and Basic authentication schemes.

api.yml
1name: api
2auth: bearer
api.yml
1name: api
2auth: basic

You can also create your own authentication schemes.

api.yml
1name: api
2auth: MyAuthScheme
3auth-schemes:
4 MyAuthScheme:
5 header: X-API-Key
6 type: string

And additionally customize the naming conventions used for standard frameworks such as Basic:

api.yml
1name: api
2auth: Basic # The name here must match the name of the auth-scheme defined below.
3auth-schemes:
4 Basic: # The name here must match the name of the auth scheme defined above.
5 scheme: basic
6 username:
7 name: clientId
8 env: MY_CLIENT_ID
9 password:
10 name: clientSecret
11 env: MY_CLIENT_SECRET

and Bearer:

api.yml
1name: api
2auth: Bearer # The name here must match the name of the auth-scheme defined below.
3auth-schemes:
4 Bearer: # The name here must match the name of the auth scheme defined above.
5 scheme: bearer
6 token:
7 name: apiKey
8 env: MY_API_KEY

Custom authentication schemes include custom OAuth integrations, too. Simply hook up your OAuth token endpoint and optionally configure environment variables to read from like so:

api.yml
1name: api
2imports:
3 auth: auth.yml
4auth: OAuthScheme
5auth-schemes:
6 OAuthScheme:
7 scheme: oauth
8 type: client-credentials
9 client-id-env: YOUR_CLIENT_ID
10 client-secret-env: YOUR_CLIENT_SECRET
11 get-token:
12 endpoint: auth.getToken # The getToken endpoint defined in the auth.yml must retrieve an access token
13 response-properties:
14 access-token: $response.access_token # The response property containing the access token. Must start with '$response.'
15 expires-in: $response.expires_in # The response property containing the expiration time. Must start with '$response.'

If you need further customization, you can specify the request-properties and response-properties to bind specific properties defined on the request and response:

api.yml
1name: api
2imports:
3 auth: auth.yml
4auth: OAuthScheme
5auth-schemes:
6 OAuthScheme:
7 scheme: oauth
8 type: client-credentials
9 clientIdEnvVar: YOUR_CLIENT_ID
10 clientSecretEnvVar: YOUR_CLIENT_SECRET
11 get-token:
12 endpoint: auth.getToken
13 request-properties:
14 client-id: $request.client_id
15 client-secret: $request.client_secret
16 response-properties:
17 access-token: $response.access_token
18 expires-in: $response.expires_in

If the expires-in property is set, the generated OAuth token provider will automatically refresh the token when it expires. Otherwise, it’s assumed that the access token is valid indefinitely.

With this, all of the OAuth logic happens automatically in the generated SDKs. As long as you configure these settings, your client will automatcally retrieve an access token and refresh it as needed.

Global headers

You can specify headers that are meant to be included on every request:

api.yml
1name: api
2headers:
3 X-App-Id: string

Global path parameters

You can specify path parameters that are meant to be included on every request:

api.yml
1name: api
2path-parameters:
3 userId: string

Global query parameters

You cannot yet specify query parameters that are meant to be included on every request. If you’d like to see this feature, please upvote this issue.

Environments

You can specify the environments where your backend is deployed. These are useful in most generated outputs, like SDKs and in Postman Collections.

api.yml
1name: api
2environments:
3 Production: https://www.yoursite.com
4 Staging:
5 docs: This staging environment is helpful for testing!
6 url: https://www.staging.yoursite.com

You can also provide a default environment:

api.yml
1name: api
2environments:
3 Production: https://www.yoursite.com
4 Staging:
5 docs: This staging environment is helpful for testing!
6 url: https://www.staging.yoursite.com
7default-environment: Production

Multiple URLs per environment

You can specify multiple URLs per environment. This is helpful if you have a microservice architecture, and you want a single SDK to interact with multiple servers.

api.yml
1environments:
2 Production:
3 urls:
4 Auth: https://auth.yoursite.com
5 Plants: https://plants.yoursite.com
6 Staging:
7 urls:
8 Auth: https://auth.staging.yoursite.com
9 Plants: https://plants.staging.yoursite.com

If you choose to use this feature, you must specify a url for each service you define:

auth.yml
1service:
2 url: Auth
3 base-path: /auth
4 ...

Error discrimination strategy

In order to generate SDKs idiomatically, Fern needs to know how to differentiate between different errors when parsing an endpoint response.

Discriminate by status code

You can specify Fern to discriminate by status code. This means on each endpoint, every error that’s listed must have a different HTTP status code.

api.yml
1name: api
2error-discrimination:
3 strategy: status-code

Discriminate by error name

You can specify Fern to discriminate by error name. If you select this strategy, then Fern will assume that every error response has an extra property denoting the error name.

If you use Fern to generate server-side code, then this option provides the most flexibility. Otherwise, you’ll probably want to use the status code discrimination strategy.

api.yml
1name: api
2error-discrimination:
3 strategy: property
4 property-name: errorName

Global errors

You can import and list errors that will be thrown by every endpoint.

api.yml
1imports:
2 commons: commons.yml
3
4errors:
5 - commons.NotFoundError
6 - commons.BadRequestError