How we use OpenAPI / Swagger for the YNAB API
Published: 2020-03-16 21:27:46

---
title: How we use OpenAPI / Swagger for the YNAB API
published: true
description: 
tags: 
---

| OpenAPI Specification | ➡ | ✅ Tests<br/>✅ Documentation<br/>✅ Client Libraries| 
|---------------|-|------| 

[YNAB](https://www.youneedabudget.com) combines software with 4 simple rules to help our users gain control their money.  Back in 2018 we released an API to help our community build things to connect their budget to other apps and services.

We built the [YNAB API](https://api.youneedabudget.com/) using the [OpenAPI Specification](https://swagger.io/docs/specification/about/) and [Swagger tooling](https://swagger.io/).  In retrospect we think it was a good decision and has provided many benefits.  This post gives a landscape of our setup and how the tooling works.

## The specification file

Everything starts with our [OpenAPI Specification](https://swagger.io/docs/specification/2-0/basic-structure/) file: [https://api.youneedabudget.com/papi/spec-v1-swagger.json](https://api.youneedabudget.com/papi/spec-v1-swagger.json).  This is a fairly simple JSON file that we edit when anything changes on the API. 
 This file defines the endpoints and the shape of request and response data.

With this spec file in place, we get access to some great tooling.

## Tests

We use a Ruby gem called [Apivore](https://github.com/westfieldlabs/apivore) to test our actual API implementation against our OpenAPI spec.  When our tests run, they exercise all the spec defined endpoints and ensure they are present and work as expected.  With Apivore, you have a `validate_all_paths` method, which does a simple validation to ensure endpoints are available and return the expected statuses.  But, you can also exercise the API by passing certain data and Apivore will ensure the shape of the data responses match your spec.  So, if your spec says an endpoint returns an array of sharks: `[{"id": 1, "name": "megamouth"},{"id": 2, "name": "hammerhead"}]` but a particular request returns a single shark: `{"id": 1, "name": "megamouth"}` a test will fail.  Another example: If you were to change the name of a field in the JSON response, a test would fail.

### Examples

Here, one of our tests ensures that requesting a non-existent budget at `/budgets/:id` returns a 404.

```ruby
it 'response with 404 for a non-existent budget' do
  expect(subject).to validate(
    :get, '/budgets/{budget_id}', 404, params.merge({
      'budget_id' => SecureRandom.uuid
    })
  )
end
```

Here, we ensure the `/budgets/:id/payees/:id` endpoint returns a payee, when requested, and also that the shape of that payee conforms to our spec:

```ruby
it 'returns a single payee' do
  expect(subject).to validate(
    :get, '/budgets/{budget_id}/payees/{payee_id}', 200, params
  )
end
```

The ability to use a tool like Apivore against our spec is a huge win because we get automatic testing.  Having this alone would be a case for using an OpenAPI spec.

## Documentation

Using [Swagger UI](https://swagger.io/tools/swagger-ui/) we are able to automatically generate our [documentation page](https://api.youneedabudget.com/v1) just by pointing to our spec.  We did make some customizations to suit our preferences but any changes to our spec file are automatically reflected on that page.

For example, the documentation for `GET /budgets/:id` shows the query parameters that can be passed, expected response status code, and the shape of the response data.

![Endpoint](https://dev-to-uploads.s3.amazonaws.com/i/p4iu8a4n4uev3pvixdro.png)

Swagger UI also has the ability to actually use the API on the page itself which is a great help for developers wanting to test something or quickly see the API responses.

For example, this is what the page looks like when requesting a specific payee:

![Endpoint Try-it Now](https://dev-to-uploads.s3.amazonaws.com/i/qkkdpwz1fpio84566lx2.png)


## Client Libraries

Arguably, Swagger tooling is most known for [Swagger Codegen](https://swagger.io/tools/swagger-codegen/), which generates client libraries for interfacing with an API.

We use Codegen to generate our [JavaScript client](https://github.com/ynab/ynab-sdk-js) and our [Ruby client](https://github.com/ynab/ynab-sdk-ruby).  And others in our community use it to build [clients for other languages](https://api.youneedabudget.com/#clients-community).

One of the nice things about Codegen is the ability to customize the generated client with the use of templates.  For example, we [specify a number of templates](https://github.com/ynab/ynab-sdk-js/tree/master/swagger-templates) to override the defaults on our JavaScript client.  This allows us to customize things to our suit our preferences.  

### TypeScript Definitions

<p>Our JavaScript client uses the <code>typescript-fetch</code> generator which, along with generating a JavaScript client usable from both Node.js and the browser, generates <a href="https://github.com/ynab/ynab-sdk-js/blob/master/dist/index.d.ts">TypeScript definition files</a>.  This is really useful because developers who are using TypeScript tooling (like <a href="https://code.visualstudio.com/">VS Code</a>) can get develop-time support.</p>

#### Examples

IntelliSense support so a developer can clearly see the available fields:

![IntelliSense](https://dev-to-uploads.s3.amazonaws.com/i/k9cu9sq08s5n8sj6s2b7.png)

Develop-time errors (a.k.a. red squiggles) so a developer can see when they have accessed a field that does not exist:

![Red squiggles](https://dev-to-uploads.s3.amazonaws.com/i/ps1palq1e1pfn1o5kfug.png)

Enums selection so a developer can easily select from a list of supported values:

![Enums](https://dev-to-uploads.s3.amazonaws.com/i/gfts2tp0rzzllyq2krmx.png)

All of this support is coming directly from the original OpenAPI specification file!

### Overall Experience

We've been pleased with our usage of an OpenAPI specification and Swagger tooling to build out our API and the ecosystem around it.  Of course, there have been a few bumps along the way and we still would like to tweak some things but the amount of benefit this tooling brings is significant and allows us to ship things more rapidly.