Open-source framework for building APIs

Overview

Fern

Fern is an open source framework that makes it easier to build APIs.

Fern allows you to

  1. Define a source-of-truth for your API
  2. Autogenerate idiomatic & typesafe clients and servers
  3. Supports WebSocket and REST APIs
  4. Easily manage backwards compatiblity

Fern is interoperable with Open API so you are never locked in.

Example Spec

Below we have written out a sample spec for a Food Delivery App.

ids:
  - MenuItemId
  - OrderId: long

types:
  DeliveryMethod:
    enum:
      - PICKUP
      - DELIVERY
  OrderStatus:
    union:
      pickup: PickupOrderStatus
      delivery: DeliveryOrderStatus
  PickupOrderStatus:
    enum:
      - PREPARING
      - READY_FOR_PICKUP
      - PICKED_UP
  DeliveryOrderStatus:
    enum:
      - PREPARING
      - ON_THE_WAY
      - DELIVERED

services:
  http:
    OrderService:
      base-path: /order
      endpoints:
        addItemToCart:
          docs: Adds a menu item to a cart.
          method: POST
          path: /add
          request:
            properties:
              menuItemId: MenuItemId
              quantity: integer
        placeOrder:
          method: POST
          path: /order/new
          request:
            properties:
              deliveryMethod: DeliveryMethod
              tip: optional<double>
          response: OrderId
          errors:
            union:
              emptyCart: EmptyCartError

  websocket:
    OrderStatusChannel:
      messages:
        subscribeToOrderStatus:
          origin: client
          body: OrderId
          response:
            properties:
              orderStatus: OrderStatus
              etaInMinutes: integer
            behavior: ongoing
          errors:
            union:
              notFound: OrderNotFoundError

errors:
  OrderNotFoundError:
    http:
      statusCode: 404
  EmptyCartError:
    http:
      statusCode: 400

The app has REST endpoints so that clients can add items to their cart and place orders. It also has a websocket channel where a client can subscribe to updates about an order's ETA.

This spec can be used to generate clients and servers.

Comments
  • GET operations cannot have a request

    GET operations cannot have a request

    Semantic error at the path (e.g. "paths./v2/link/provider/oauth/{oauth_provider}.get.requestBody") GET operations cannot have a requestBody.

    fern-check 
    opened by dannysheridan 10
  • UX for multiple workspaces

    UX for multiple workspaces

    • workspaces should be aliased in fern.config.json I could see fern.config.json changing to
    {
      "workspaces": {
         "coordinator-api": "/path/to/coordinator"
      }
    }
    

    We can get rid of the "auto find all your .fernrcs" which also creates problems for us in this repo when we add a ton of nested fern api workspaces for testing.

    • fern init asks you for an api name and will add to the fern.config.json and initialize a new workspace

    • fern generate asks you what workspace you want to generate for if you forget to specify!

    definition 
    opened by dsinghvi 4
  • Migrations should result in a valid definition

    Migrations should result in a valid definition

    Either use placeholders (e.g. when a request requires a name, make it name: DEFAULTRESPONSENAMECHANGEME or have the CLI prompt the user for the parameter value)

    fern-upgrade 
    opened by dannysheridan 3
  • [Docs] Add initial configuration and content for documentation V2

    [Docs] Add initial configuration and content for documentation V2

    Summary

    This PR enables the updated documentation setup for Fern, to be available at buildwithfern.com/docs and currently for preview at fern.mintlify.app

    docs-v2

    🚀 Setup

    Simply merge in this PR and your documentation will be connected!

    👩‍💻 Development

    Install the Mintlify CLI to preview the documentation changes locally. To install, use the following command

    npm i mintlify -g
    

    Run the following command at the root of your documentation (where mint.json is)

    mintlify dev
    

    😎 Publishing Changes

    Changes will be deployed to production automatically after pushing to the default branch.

    You can also preview changes using PRs, which generates a preview link of the docs.

    Troubleshooting

    • Mintlify dev isn't running - Run mintlify install it'll re-install dependencies.
    • Mintlify dev is updating really slowly - Run mintlify clear to clear the cache.
    opened by hanywang2 3
  • Fern generate fails inelegantly

    Fern generate fails inelegantly

    dsheridan@air-diesel fern-climatiq % fern generate
    fern generate [workspaces...]
    
    Generate typesafe servers and clients
    
    Positionals:
      workspaces  If omitted, every workspace specified in the project-level configu
                  ration (fern.config.json) will be processed. [array] [default: []]
    
    Options:
          --help        Show help                                          [boolean]
          --keepDocker  If true, Docker containers are not removed after generation.
                         This is ignored for remote generation.
                                                          [boolean] [default: false]
          --local       If true, code is generated using Docker on this machine.
                                                          [boolean] [default: false]
      -v, --version     Show version number                                [boolean]
    
    [AxiosError: Request failed with status code 500] {
      code: 'ERR_BAD_RESPONSE',
      config: {
        transitional: {
          silentJSONParsing: true,
          forcedJSONParsing: true,
          clarifyTimeoutError: false
        },
        adapter: [Function: httpAdapter],
        transformRequest: [ [Function: transformRequest] ],
        transformResponse: [ [Function: transformResponse] ],
        timeout: 0,
        xsrfCookieName: 'XSRF-TOKEN',
        xsrfHeaderName: 'X-XSRF-TOKEN',
        maxContentLength: -1,
        maxBodyLength: -1,
        env: { FormData: [Function] },
        validateStatus: [Function: validateStatus],
        headers: {
          Accept: 'application/json, text/plain, */*',
          'Content-Type': 'multipart/form-data; boundary=--------------------------350140785974621542563728',
          'User-Agent': 'axios/0.27.2',
          'content-type': 'multipart/form-data; boundary=--------------------------350140785974621542563728'
        },
        method: 'post',
        url: 'https://fiddle-coordinator.buildwithfern.com/api/remote-gen/jobs/31fa1f35-46c8-4339-aed1-67a37ed97254/start',
        data: FormData3 {
          _overheadLength: 103,
          _valueLength: 33613,
          _valuesToMeasure: [],
          writable: false,
          readable: true,
          dataSize: 0,
          maxDataSize: 2097152,
          pauseStreams: true,
          _released: true,
          _streams: [],
          _currentStream: null,
          _insideLoop: false,
          _pendingNext: false,
          _boundary: '--------------------------350140785974621542563728',
          _events: [Object: null prototype],
          _eventsCount: 1
        }
      },
      request: <ref *1> ClientRequest {
        _events: [Object: null prototype] {
          abort: [Function (anonymous)],
          aborted: [Function (anonymous)],
          connect: [Function (anonymous)],
          error: [Function (anonymous)],
          socket: [Function (anonymous)],
          timeout: [Function (anonymous)],
          prefinish: [Function: requestOnPrefinish]
        },
        _eventsCount: 7,
        _maxListeners: undefined,
        outputData: [],
        outputSize: 0,
        writable: true,
        destroyed: false,
        _last: true,
        chunkedEncoding: true,
        shouldKeepAlive: false,
        maxRequestsOnConnectionReached: false,
        _defaultKeepAlive: true,
        useChunkedEncodingByDefault: true,
        sendDate: false,
        _removedConnection: false,
        _removedContLen: false,
        _removedTE: false,
        _contentLength: null,
        _hasBody: true,
        _trailer: '',
        finished: true,
        _headerSent: true,
        _closed: false,
        socket: TLSSocket {
          _tlsOptions: [Object],
          _secureEstablished: true,
          _securePending: false,
          _newSessionPending: false,
          _controlReleased: true,
          secureConnecting: false,
          _SNICallback: null,
          servername: 'fiddle-coordinator.buildwithfern.com',
          alpnProtocol: false,
          authorized: true,
          authorizationError: null,
          encrypted: true,
          _events: [Object: null prototype],
          _eventsCount: 10,
          connecting: false,
          _hadError: false,
          _parent: null,
          _host: 'fiddle-coordinator.buildwithfern.com',
          _readableState: [ReadableState],
          _maxListeners: undefined,
          _writableState: [WritableState],
          allowHalfOpen: false,
          _sockname: null,
          _pendingData: null,
          _pendingEncoding: '',
          server: undefined,
          _server: null,
          ssl: [TLSWrap],
          _requestCert: true,
          _rejectUnauthorized: true,
          parser: null,
          _httpMessage: [Circular *1],
          [Symbol(res)]: [TLSWrap],
          [Symbol(verified)]: true,
          [Symbol(pendingSession)]: null,
          [Symbol(async_id_symbol)]: 190,
          [Symbol(kHandle)]: [TLSWrap],
          [Symbol(lastWriteQueueSize)]: 0,
          [Symbol(timeout)]: null,
          [Symbol(kBuffer)]: null,
          [Symbol(kBufferCb)]: null,
          [Symbol(kBufferGen)]: null,
          [Symbol(kCapture)]: false,
          [Symbol(kSetNoDelay)]: false,
          [Symbol(kSetKeepAlive)]: true,
          [Symbol(kSetKeepAliveInitialDelay)]: 60,
          [Symbol(kBytesRead)]: 0,
          [Symbol(kBytesWritten)]: 0,
          [Symbol(connect-options)]: [Object]
        },
        _header: 'POST /api/remote-gen/jobs/31fa1f35-46c8-4339-aed1-67a37ed97254/start HTTP/1.1\r\n' +
          'Accept: application/json, text/plain, */*\r\n' +
          'content-type: multipart/form-data; boundary=--------------------------350140785974621542563728\r\n' +
          'User-Agent: axios/0.27.2\r\n' +
          'Host: fiddle-coordinator.buildwithfern.com\r\n' +
          'Connection: close\r\n' +
          'Transfer-Encoding: chunked\r\n' +
          '\r\n',
        _keepAliveTimeout: 0,
        _onPendingData: [Function: nop],
        agent: Agent {
          _events: [Object: null prototype],
          _eventsCount: 2,
          _maxListeners: undefined,
          defaultPort: 443,
          protocol: 'https:',
          options: [Object: null prototype],
          requests: [Object: null prototype] {},
          sockets: [Object: null prototype],
          freeSockets: [Object: null prototype] {},
          keepAliveMsecs: 1000,
          keepAlive: false,
          maxSockets: Infinity,
          maxFreeSockets: 256,
          scheduling: 'lifo',
          maxTotalSockets: Infinity,
          totalSocketCount: 1,
          maxCachedSessions: 100,
          _sessionCache: [Object],
          [Symbol(kCapture)]: false
        },
        socketPath: undefined,
        method: 'POST',
        maxHeaderSize: undefined,
        insecureHTTPParser: undefined,
        path: '/api/remote-gen/jobs/31fa1f35-46c8-4339-aed1-67a37ed97254/start',
        _ended: true,
        res: IncomingMessage {
          _readableState: [ReadableState],
          _events: [Object: null prototype],
          _eventsCount: 4,
          _maxListeners: undefined,
          socket: [TLSSocket],
          httpVersionMajor: 1,
          httpVersionMinor: 1,
          httpVersion: '1.1',
          complete: true,
          rawHeaders: [Array],
          rawTrailers: [],
          aborted: false,
          upgrade: false,
          url: '',
          method: null,
          statusCode: 500,
          statusMessage: '',
          client: [TLSSocket],
          _consuming: false,
          _dumped: false,
          req: [Circular *1],
          responseUrl: 'https://fiddle-coordinator.buildwithfern.com/api/remote-gen/jobs/31fa1f35-46c8-4339-aed1-67a37ed97254/start',
          redirects: [],
          [Symbol(kCapture)]: false,
          [Symbol(kHeaders)]: [Object],
          [Symbol(kHeadersCount)]: 14,
          [Symbol(kTrailers)]: null,
          [Symbol(kTrailersCount)]: 0
        },
        aborted: false,
        timeoutCb: null,
        upgradeOrConnect: false,
        parser: null,
        maxHeadersCount: null,
        reusedSocket: false,
        host: 'fiddle-coordinator.buildwithfern.com',
        protocol: 'https:',
        _redirectable: Writable {
          _writableState: [WritableState],
          _events: [Object: null prototype],
          _eventsCount: 3,
          _maxListeners: undefined,
          _options: [Object],
          _ended: true,
          _ending: true,
          _redirectCount: 0,
          _redirects: [],
          _requestBodyLength: 33772,
          _requestBodyBuffers: [],
          _onNativeResponse: [Function (anonymous)],
          _currentRequest: [Circular *1],
          _currentUrl: 'https://fiddle-coordinator.buildwithfern.com/api/remote-gen/jobs/31fa1f35-46c8-4339-aed1-67a37ed97254/start',
          [Symbol(kCapture)]: false
        },
        [Symbol(kCapture)]: false,
        [Symbol(kNeedDrain)]: true,
        [Symbol(corked)]: 0,
        [Symbol(kOutHeaders)]: [Object: null prototype] {
          accept: [Array],
          'content-type': [Array],
          'user-agent': [Array],
          host: [Array]
        }
      },
      response: {
        status: 500,
        statusText: '',
        headers: {
          date: 'Tue, 02 Aug 2022 16:26:03 GMT',
          'content-type': 'application/json',
          'content-length': '79',
          connection: 'close',
          'access-control-allow-origin': '*',
          'access-control-allow-headers': '*',
          'access-control-allow-methods': 'OPTIONS,GET,PUT,POST,DELETE,HEAD'
        },
        config: {
          transitional: [Object],
          adapter: [Function: httpAdapter],
          transformRequest: [Array],
          transformResponse: [Array],
          timeout: 0,
          xsrfCookieName: 'XSRF-TOKEN',
          xsrfHeaderName: 'X-XSRF-TOKEN',
          maxContentLength: -1,
          maxBodyLength: -1,
          env: [Object],
          validateStatus: [Function: validateStatus],
          headers: [Object],
          method: 'post',
          url: 'https://fiddle-coordinator.buildwithfern.com/api/remote-gen/jobs/31fa1f35-46c8-4339-aed1-67a37ed97254/start',
          data: [FormData3]
        },
        request: <ref *1> ClientRequest {
          _events: [Object: null prototype],
          _eventsCount: 7,
          _maxListeners: undefined,
          outputData: [],
          outputSize: 0,
          writable: true,
          destroyed: false,
          _last: true,
          chunkedEncoding: true,
          shouldKeepAlive: false,
          maxRequestsOnConnectionReached: false,
          _defaultKeepAlive: true,
          useChunkedEncodingByDefault: true,
          sendDate: false,
          _removedConnection: false,
          _removedContLen: false,
          _removedTE: false,
          _contentLength: null,
          _hasBody: true,
          _trailer: '',
          finished: true,
          _headerSent: true,
          _closed: false,
          socket: [TLSSocket],
          _header: 'POST /api/remote-gen/jobs/31fa1f35-46c8-4339-aed1-67a37ed97254/start HTTP/1.1\r\n' +
            'Accept: application/json, text/plain, */*\r\n' +
            'content-type: multipart/form-data; boundary=--------------------------350140785974621542563728\r\n' +
            'User-Agent: axios/0.27.2\r\n' +
            'Host: fiddle-coordinator.buildwithfern.com\r\n' +
            'Connection: close\r\n' +
            'Transfer-Encoding: chunked\r\n' +
            '\r\n',
          _keepAliveTimeout: 0,
          _onPendingData: [Function: nop],
          agent: [Agent],
          socketPath: undefined,
          method: 'POST',
          maxHeaderSize: undefined,
          insecureHTTPParser: undefined,
          path: '/api/remote-gen/jobs/31fa1f35-46c8-4339-aed1-67a37ed97254/start',
          _ended: true,
          res: [IncomingMessage],
          aborted: false,
          timeoutCb: null,
          upgradeOrConnect: false,
          parser: null,
          maxHeadersCount: null,
          reusedSocket: false,
          host: 'fiddle-coordinator.buildwithfern.com',
          protocol: 'https:',
          _redirectable: [Writable],
          [Symbol(kCapture)]: false,
          [Symbol(kNeedDrain)]: true,
          [Symbol(corked)]: 0,
          [Symbol(kOutHeaders)]: [Object: null prototype]
        },
        data: {
          _error: '_unknown',
          _errorInstanceId: '724512fe-d0fc-4ecf-978f-388f4f2cb7fe'
        }
      }
    }
    
    bug fern-generate 
    opened by dannysheridan 3
  • Rename Workspace and WorkspaceDefinition to more descriptive names

    Rename Workspace and WorkspaceDefinition to more descriptive names

    https://www.notion.so/usebirch/Fern-Workspace-Terminology-dae519a16ce1421fac15f377c0c82550

    repo/
      api/
        .fernrc
        src/
          imdb.yml
    
    1. What do we call the api folder, which contains the Fernrc and the definition?
      1. Workspace
    2. What do we call the .fernrc?
      1. Workspace Configuration
    3. What do we call the the src/ folder?
      1. API Definition
    opened by zachkirsch 3
  • improvement: init has lang server comment

    improvement: init has lang server comment

    @zachkirsch i have just hardcoded the url to the next release and we'll have to remember to bump this whenever the json schema changes. The better option i think is to inject a REF constant into the bundle through webpack's DefinePlugin. Ref should either be the commit hash or the tag which we pass to the init command. What do you think of that approach? If good, can take a stab at it.

    opened by dsinghvi 3
  • improvement: validation rule to check if imports actually exist

    improvement: validation rule to check if imports actually exist

    trying to implement a validation rule to check if imported files actually exist, had to do a couple of interesting things: (1) pass down absoluteDefinitionPath all the way down as RuleRunnerArgs , (2) once i had the absolute path, i could append the imported file path and then check if the file exists but this introduces async into the rules which messes with a lot of the types. curious how you’d approach this instead

    Fixes https://github.com/fern-api/fern/issues/387

    opened by dsinghvi 3
  • fern-release didn't run correctly, but didn't throw an error

    fern-release didn't run correctly, but didn't throw an error

    https://github.com/fern-primer/primer-api/actions/runs/3783614880/jobs/6432301003

    I was surprised when it seemed like fern release ran, but it didn't.

    fern-generate 
    opened by dannysheridan 2
  • I can't specify a currency code

    I can't specify a currency code

    I have a property that is a currency. I'm using the ISO 4217 three-letter standard (e.g. USD, EUR) and would like to be able to specify that in my Fern Definition.

    definition 
    opened by dannysheridan 2
  • I can't specify standard codes for countries

    I can't specify standard codes for countries

    countries is an list of string ISO 3166-1 alpha-2 which is a

    List of countries in ISO 3166-1 alpha-2 format covered by this coverage configuration.
    

    Example: ["DE","FR","US"]

    Today in Fern, the most specific I can get is string.

    Context: OpenAPI supports this and renders it in Stoplight docs. (Example https://developers.gigs.com/docs/api/bc5c988e713ed-list-all-plans#response-body )

    opened by dannysheridan 2
  • Add referenced examples with $

    Add referenced examples with $

    TODO:

    • [x] Handle $ in existing fern check rules
    • [ ] Add fern check rule for non-existing reference
    • [ ] Add fern check role for duplicate example names
    opened by zachkirsch 0
  • I lost a commonly referenced type during a migration

    I lost a commonly referenced type during a migration

    https://github.com/ravenappdev/raven-api/pull/31/commits/b1db4aba4dda30a9e89883ebcff211e04123f273

    The migration moved Device in-line for a request. I reference that type multiple times in the API definition and thus failed fern check now. It feels like the migration caused me a headache to go figure out how to fix this.

    fern-upgrade 
    opened by dannysheridan 1
  • Help me remember to .fernignore files I've manually added

    Help me remember to .fernignore files I've manually added

    As a user, if I add custom files to the repo, it would be nice have a reminder from Fern that they need to be added to .fernignore or else they'll be overwritten.

    An example is adding a GitHub Action and then forgetting to add .ferignore so my file is deleted: https://github.com/fern-primer/primer-openapi/commit/a7cff3d9072538ce21e8d547bd3b7a38e64d9438

    fern-check 
    opened by dannysheridan 0
  • Talk up server interfaces in Fern vs. OpenAPI

    Talk up server interfaces in Fern vs. OpenAPI

    for server-side API development I think we should focus on server side interfaces earlier and explain why they are different from server side stubs . I think we need to explicitly talk about how generated code and written code can live in separate places and not conflict.

    opened by zachkirsch 0
Releases(0.3.0-rc14)
Owner
Fern
Open-source framework for building APIs
Fern
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
Collection of My Open-Source APIs.

GTAVModFinder Experimental mod finder from gta5-mods.com Installation ⚒ npm update npm install gta5mods-finder NPM ?? NPM Page - Phaticusthiccy's gta

Thiccy 2 Dec 26, 2021
Free, open-source client or server-side APIs to "lint" user input.

passbird Free, open-source client or server-side APIs to lint user input. Right now, you can check type for an email address i.e., either of disposabl

Vaibhav Pandey 1 Dec 26, 2021
Reference for How to Write an Open Source JavaScript Library - https://egghead.io/series/how-to-write-an-open-source-javascript-library

Reference for How to Write an Open Source JavaScript Library The purpose of this document is to serve as a reference for: How to Write an Open Source

Sarbbottam Bandyopadhyay 175 Dec 24, 2022
An Open-Source Platform to certify open-source projects.

OC-Frontend This includes the frontend for Open-Certs. ?? After seeing so many open-source projects being monetized ?? without giving any recognition

Open Certs 15 Oct 23, 2022
Shikhar 4 Oct 9, 2022
This is a project for open source enthusiast who want to contribute to open source in this hacktoberfest 2022. 💻 🎯🚀

HACKTOBERFEST-2022-GDSC-IET-LUCKNOW Beginner-Hacktoberfest Need Your first pr for hacktoberfest 2k22 ? come on in About Participate in Hacktoberfest b

null 8 Oct 29, 2022
Pagination Manager is an useful framework for improving the use of object pagination in APIs like Discord.

Pagination Manager Pagination Manager is an useful framework for improving the use of object pagination in APIs like Discord. Lightweight module, ES6

tnfAngel 4 Jul 26, 2022
A recreation of a startpage posted on Reddit without the source, so I rewrote it in Next.js + Tailwind for the open source community.

Startpage "Figma Balls" Rewrite Why Did I Make This I saw a startpage posted on the subreddit r/startpages that I thought looked nice, but when I look

Thomas Leon Highbaugh 5 Mar 29, 2022
Let's participate in Hacktoberfest and contribute to open-source. Star the repo and open a PR to get accepted.

Let's Contribute To Open-source First Contributions This project aims to simplify and guide the way, beginners can make their first contribution towar

Ehmad Saeed⚡ 5 Dec 3, 2022
BOOTFLAT is an open source Flat UI KIT based on Bootstrap 3.3.0 CSS framework

BOOTFLAT is an open source Flat UI KIT based on Bootstrap 3.3.0 CSS framework. It provides a faster, easier and less repetitive way for web developers to create elegant web apps.

bootflat 4.3k Dec 25, 2022
ToolJet an open-source low-code framework to build and deploy internal tools quickly without much effort from the engineering teams

ToolJet is an open-source low-code framework to build and deploy internal tools quickly without much effort from the engineering teams. You can connect to your data sources, such as databases (like PostgreSQL, MongoDB, Elasticsearch, etc), API endpoints (ToolJet supports importing OpenAPI spec & OAuth2 authorization), and external services (like Stripe, Slack, Google Sheets, Airtable) and use our pre-built UI widgets to build internal tools.

ToolJet 15.6k Jan 3, 2023
VueJS based Shopsystem for the Athena Framework, absolute free and open source! Full Database Integration (MongoDB)

Open Source Shop Herewith we bring a free store system for the Athena Framework which is under the MIT license and thus can be completely modified and

Der Lord! 13 Oct 31, 2022
Open-source Solana Pay framework

Bedrock Bedrock is a framework for Solana Pay that: Standardizes the methodology used to create transaction requests Provides implementations for many

Bedrock Foundation 11 Nov 26, 2022
Preline UI is an open-source set of prebuilt UI components based on the utility-first Tailwind CSS framework.

Preline UI is an open-source set of prebuilt UI components based on the utility-first Tailwind CSS framework. Why use Preline UI? Based on the Tailwin

Html Stream 1.1k Jan 3, 2023
A fast, feature rich and simple framework for building dynamic browser applications.

hyperdom A simple, fast, feature rich framework for building dynamic browser applications. Hyperdom supports a simple event-update-render cycle, promi

Featurist 163 Nov 11, 2022
A easy-to-use framework for building immersive decentralized applications

A easy-to-use framework for building immersive decentralized applications

Sonr 624 Dec 27, 2022