Fern Definition

Endpoints

In Fern, you organize related endpoints into a Service. This grouping improves clarity and makes the generated SDKs more idiomatic.

Service definition

Each service defines:

  1. A base-path: A common prefix for all the endpoints' HTTP paths
  2. Whether the service requires authentication
  3. Endpoints
1
2
3
4
service:
  base-path: /users
  auth: false
  endpoints: {}

Endpoints

An endpoint includes:

  • A URL path (Optionally including path parameters)
  • An HTTP Method
  • Request information (Optional)
    • Query-parameters
    • Headers
    • Request body
  • Successful (200) response information (Optional)
  • Error (non-200) responses that this endpoint might return (Optional)

URL path

Each endpoint has a URL path.

1
2
3
4
5
6
7
service:
  base-path: /users
  auth: false
  endpoints:
    getAllUsers:
      path: /all # <---
      method: GET

The full path for the endpoint is the concatenation of:

  • The environment URL
  • The service base-path
  • The endpoint path

Path parameters

Supply path parameters for your endpoints to create dynamic URLs.

1
2
3
4
5
6
7
8
9
service:
  base-path: /users
  auth: false
  endpoints:
    getUser:
      path: /{userId} # <---
      path-parameters: # <---
        userId: string
      method: GET

Services can also have path-parameters:

1
2
3
4
5
6
service:
  base-path: /projects/{projectId}
  path-parameters:
    projectId: string
  auth: false
  endpoints: ...

Query parameters

Each endpoint can specify query parameters:

1
2
3
4
5
6
7
8
9
10
11
12
service:
  base-path: /users
  auth: false
  endpoints:
    getAllUsers:
      path: /all
      method: GET
      request:
        # this name is required for idiomatic SDKs
        name: GetAllUsersRequest
        query-parameters:
          limit: optional<integer>

allow-multiple

Use allow-multiple to specify that a query parameter is allowed multiple times in the URL, as in ?filter=jane&filter=smith. This will alter the generated SDKs so that consumers can provide multiple values for the query parameter.

1
2
3
4
query-parameters:
  filter:
    type: string
    allow-multiple: true # <---

Auth

Each endpoint can override the auth behavior specified in the service.

1
2
3
4
5
6
7
8
9
10
service:
  base-path: /users
  auth: false
  endpoints:
    getMe:
      path: ""
      method: GET
      # This endpoint will be authed
      auth: true
      docs: Return the current user based on Authorization header.

Headers

Each endpoint can specify request headers:

1
2
3
4
5
6
7
8
9
10
11
12
service:
  base-path: /users
  auth: false
  endpoints:
    getAllUsers:
      path: /all
      method: GET
      request:
        # this name is required for idiomatic SDKs
        name: GetAllUsersRequest
        headers:
          X-Endpoint-Header: string

Services can also specify request headers. These headers will cascade to the service's endpoints.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 service:
   base-path: /users
   auth: false
+    headers:
+      X-Service-Header: string
   endpoints:
     getAllUsers:
       path: /all
       method: GET
       request:
         # this name is required for idiomatic SDKs
         name: GetAllUsersRequest
         headers:
           X-Endpoint-Header: string

Request body

Endpoints can specify a request body type.

1
2
3
4
5
6
7
8
9
10
service:
  base-path: /users
  auth: false
  endpoints:
    setUserName:
      path: /{userId}/set-name
      path-parameters:
        userId: string
      method: POST
      request: string # <---

Inlining a request body

If the request body is an object, you can inline the type declaration. This makes the generated SDKs a bit more idiomatic.

1
2
3
4
5
6
7
8
9
10
11
12
13
service:
  base-path: /users
  auth: false
  endpoints:
    createUser:
      path: /create
      method: POST
      request:
        # this name is required for idiomatic SDKs
        name: CreateUserRequest
        body:
          properties:
            userName: string

Multipart form uploads

If the request involves uploading a file, use the file type in your request body.

1
2
3
4
5
6
7
8
9
10
11
12
service:
  base-path: /documents
  auth: false
  endpoints:
    uploadDocument:
      path: /upload
      method: POST
      request:
        name: UploadDocumentRequest
        body:
          properties:
            file: file # <------ 

Success response

Endpoints can specify a response, which is the type of the body that will be returned on a successful (200) call.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
service:
  base-path: /users
  auth: false
  endpoints:
    getAllUsers:
      path: /all
      method: GET
      response: list<User>

types:
  User:
    properties:
      userId: string
      name: string

Error responses

Endpoints can specify error responses, which detail the non-200 responses that the endpoint might return.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
service:
  base-path: /users
  auth: false
  endpoints:
    getUser:
      path: /{userId}
      path-parameters:
        userId: string
      method: GET
      response: User
      errors:
        - UserNotFoundError

types:
  User:
    properties:
      userId: string
      name: string

errors:
  UserNotFoundError:
    status-code: 404

You can learn more about how to define errors on the Errors page.

Specifying examples

When you declare an example, you can also specify some examples of how that endpoint might be used. These are used by the compiler to enhance the generated outputs. Examples will show up as comments in your SDKs, API documentation, and Postman collection.

You may add examples for endpoints, types, and errors.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
service:
  base-path: /users
  auth: false
  endpoints:
    getUser:
      path: /{userId}
      path-parameters:
        userId: string
      method: GET
      response: User
      errors:
        - UserNotFoundError
      examples: # <---
        - path-parameters:
            userId: alice-user-id
          response:
            body:
              userId: alice-user-id
              name: Alice

types:
  User:
    properties:
      userId: string
      name: string

errors:
  UserNotFoundError:
    status-code: 404

If you're adding an example to an endpoint and the type already has an example, you can reference it using $.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
service:
  auth: true
  base-path: /address
  endpoints:
    create:
      method: POST
      path: ""
      request: CreateAddress
      response: Address
      examples:
        - request: $CreateAddress.WhiteHouse
          response:
            body: $Address.WhiteHouseWithID

  CreateAddress:
    properties:
      street1: string
      street2: optional<string>
      city: string
      state: string
      postalCode: string
      country: string
      isResidential: boolean
    examples:
      - name: WhiteHouse
        value:
          street1: 1600 Pennsylvania Avenue NW
          city: Washington DC
          state: Washington DC
          postalCode: "20500"
          country: US
          isResidential: true

  Address:
    extends: CreateAddress
    properties:
      id:
        type: uuid
        docs: The unique identifier for the address.
    examples:
      - name: WhiteHouseWithID
        value:
          id: 65ce514c-41e3-11ee-be56-0242ac120002
          street1: 1600 Pennsylvania Avenue NW
          city: Washington DC
          state: Washington DC
          postalCode: "20500"
          country: US
          isResidential: true

Examples contain all the information about the endpoint call, including the request body, path paramters, query parameters, headers, and response body.

1
2
3
4
5
6
7
8
9
10
examples:
  - path-parameters:
      userId: some-user-id
    query-parameters:
      limit: 50
    headers:
      X-My-Header: some-value
    response:
      body:
        response-field: hello

Failed examples

You can also specify examples of failed endpoints calls. Add the error property to a response example to designate which failure you're demonstrating.

1
2
3
4
5
6
7
8
9
examples:
  - path-parameters:
      userId: missing-user-id
    response:
      error: UserNotFoundError # <--

errors:
  UserNotFoundError:
    status-code: 404

If the error has a body, then you must include the body in the example.

1
2
3
4
5
6
7
8
9
10
11
examples:
  - path-parameters:
      userId: missing-user-id
    response:
      error: UserNotFoundError
      body: "User with id `missing-user-id` was not found" # <--

errors:
  UserNotFoundError:
    status-code: 404
    type: string # <--

Referencing examples from types

To avoid duplication, you can reference examples from types using $.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
service:
  base-path: /users
  auth: true
  endpoints:
    getUser:
      method: GET
      path: /{userId}
      path-parameters:
        userId: UserId
      examples:
        - path-parameters:
            userId: $UserId.Example1 # <---

types:
  UserId:
    type: integer
    examples:
      - name: Example1
        value: user-id-123