API Sorrows
Tags: api, rest, swagger, codequality
Published: 2020-07-18 03:53:36

The longer I write code, the more it feels that my life can essentially be boiled down to the act of connecting APIs.  If I'm not connecting to our own _internal_ APIs, I'm fetching data from some sort of external/public API.  

For the most part, this process seems to get progressively better every single year.  I have horrible war stories to tell about SOAP and deep battle scars from XMLHttpRequest.  But I survived those trials and the API world today feels so much more... _accommodating_.

But that doesn't mean that everything in API Land is Rainbows & Lollipops.  There are still headaches lurking out there.  And because this blog is my own, personal, unpaid, self-administered therapy, I'm going to spend a few minutes venting my spleen.

If you ever find yourself in a position to actually _write_ endpoints, then I want you to listen up.  Because I'm going to lay out a step-by-step guide whereby you can make all the consumers of those endpoints _hate your guts_.
<br/><br/>

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/sb1l71jv2yn1pb57dm0z.png)
## Show Me The Dang Swagger
If you are publishing REST endpoints, I have one thing, and only one thing, to ask of you:
<br/>

> Where's the Swagger file???

<br/>If your answer starts to tail off into some diatribe about the detailed documentation site that your team spent six months building, Ima stop you right there and ask again:
<br/>

> _Where's the Swagger file???_

<br/>If you're bold enough to think that you can mollify me with some promise of Swagger docs that might be available _after_ the endpoints are "officially" released, I'll make sure to show you the rising anger in my face before I say:
<br/>

> I'm sorry.  Maybe we're speaking different languages.  Because you definitely don't seem to be processing the words that are coming outta my mouth.  So I'm going to ask one more time with all the civility I can muster:  **_Where is the got-dang Swagger file???_**

<br/>In the last couple of years, I can't even tell you how many times I've been asked to integrate with some set of vendor/partner REST endpoints.  And as soon as I ask, "Where's the Swagger file?" everyone looks at me like I showed up at a civil rights protest in blackface.  

I understand that Swagger files are not the "end-all / be-all" of API documentation.  But if you're publishing REST endpoints, they should be the _starting_ point and the _ending_ point of all documentation.  If you wanna throw up some detailed "How to use our API" website... _great_!  But don't you dare tell me that your DIY site is meant to be a _replacement_ for good ol' fashioned Swagger files.

For REST endpoints, Swagger files are not a "nice to have".  They are a basic requirement.
<br/><br/>

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/xorgwpvo1jn3iyfw8rk1.jpg)
## Leave the Writing To Stephen King
As much as I cherish a good Swagger file, this can also lead to a terribly-false sense of security.  (And epic headaches down-the-road.)  Some of my most painful programming experiences in the last several years occurred when I was given a Swagger file - and the Swagger file was an aspirational work of fiction.

I'm frequently required to integrate with vendors/endpoints _that haven't yet been deployed_.  And you know what??  That's _OK_.  Or at least, it _should_ be OK.  Because, as long as I have a detailed Swagger file at my disposal, I can crank out vast amounts of functionality designed to interact with your _future_ endpoints.

Here's where that "OK" has fallen apart - badly.

Some vendor/partner sends me a Swagger file and I proceed to write vast quantities of code designed to interact with that API.  Then the API goes live, and... my code doesn't work.  _NONE_ of my code works.  

My bosses, and my clients, and anyone else monitoring my work immediately believe that I've "dropped the ball" and I start scrambling to figure out what went wrong.  That's when I realize that the Swagger files I've been given were as accurate as a Trump speech.

But how can this be?  Shouldn't Swagger files be a real-time reflection _of the actual code_??  Well... they are - if your Swagger files are _automatically generated_.  

In other words, I've had too many experiences where someone on the API team was literally writing the Swagger files, _by hand_, to reflect how their API would presumably behave.  _Of course_, by the time that I could finally hit their endpoints, in real time, the Swagger files I'd been given (and against which I was coding) were a complete fiction.  The behavior of the _live_ endpoints bore no resemblance to the responses that were defined in the Swagger files.

This is the 2020s, people.  _No one_ should be manually **writing** API documentation anymore.  There are plenty of packages out there that will document your endpoints _dynamically_, and in _real time_, based on the actual code that you've promoted to production.  

I'd bet good money that the documentation someone manually _wrote_ for your application five years ago is, today, practically useless.  Similarly, the act of manually _writing_ API documentation is a complete-and-utter waste of time.

Don't _write_ API documentation.  _Generate_ it.  Automatically.  I fully understand that this can still cause last-minute issues if you make last-minute changes to your code.  But at a minimum, I can feel secure in the knowledge that, anytime I hit your dynamic API documentation site, I'm at least seeing an accurate representation of how your endpoints behave _at this moment in time_.  If you're manually _writing_ your API documentation, it may as well be scribbled, on schoolyard pavement, in chalk for all I care.
<br/><br/>

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ji0ffao1f1m9lz1jz9bf.png)
## Manufacturing Bottlenecks
Those who write APIs spend _a lot_ of time thinking about issues surrounding performance and usage.  After all, an API is, basically, an open invitation for the whole world to bombard your servers.  So it makes sense to be hyper-vigilante, _right_???

Well...

I've seen too many scenarios where the API architects were content to create rote, mindless limitations.  These limitations often make sense when you look at individual API calls - _in a vacuum_.  But when you widen the lens - _just a little bit_ - it quickly becomes apparent that these limitations are actually **harmful**.

Lemme give you a real-world example from Spotify's API.  [Note: If you've noticed that I'm picking on Spotify a lot lately, it's only because I've been building some new tools around their API.  So it brings all of their shortcomings into sharp focus in my mind.]

All of the Spotify endpoints have limits on the total number of records that can be returned.  For example, if you want to retrieve all of the tracks in a given playlist, there's an endpoint for that.  _But_... that endpoint will retrieve no more than 100 tracks at a time. 

Maybe that makes sense to you.  After all, you don't want someone launching a single API call that returns, say, 10,000 records, _right_??  Well... think about the alternative.

You see, most people who use Spotify frequently have their music sorted into _playlists_.  And those playlists frequently hold well-over 100 tracks each.  

But if I'm writing a feature that's designed to interact with a given user's _playlist_, what are the odds that my feature will only need to grab the _first_ 100 tracks from that playlist?  Or the _last_ 100 tracks?  In fact, if I'm trying to do _playlist management_, what are the odds that _any_ operation I execute can suffice to only know about 100 tracks in that playlist??

The far more likely scenario is that, if I'm building functionality that's designed to help you manage a playlist, then I probably need to know about _all_ the tracks in that playlist.  If I need to know _all_ the tracks in a 500-track playlist, and the endpoint will only ever let me return 100 tracks at a time, then my application will have no choice _but to make five consecutive calls to the same endpoint_ - probably in fairly-rapid succession.

So in this scenario, is the 100-track limit doing anything to aid the performance of your server?  If _one request_ must be converted into _five rapid-fire requests_, are your record limits actually serving their purpose???

To be clear, I understand that there are times when record limits make perfect sense.  If, say, you have an API that allows people to search _all_ songs recorded over the last century (which would be _millions_ of records), then maybe it makes perfect sense that you would limit any particular search to a given number of records.

But when you're dealing with something like a user's playlist, you typically need to get _all_ of the tracks in that playlist.  And limiting the return set only forces the consumer to spawn more (expensive) round-trip calls to your endpoint.
<br/><br/>

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/253cdj1xvvwte1jdssxi.jpg)
## Don't Be A REST Cultist
Look... I could write another long diatribe about what's great - and what _sucks_ - about REST.  And I assume that I probably will at some point in the future.  But for now, suffice it to say that slavishly following _every minute detail_ of the REST Purists' Bible can make life hell for your consumers.

A perfect example of this is what I call the REST 404 Paradox.  

In theory, when a resource isn't available, you're supposed to return a `404`.  But, depending upon how you read the REST standards, this also means that you should return a `404` _when a search returns no results_.  Quite frankly, that's annoying AF.

I thoroughly understand that this URL:

`GET https://myapi.com/v1/users/e91781a4-21e7-427a-b970-d92fca15c556/`

Will return a `404` if there is _no_ user with that GUID.  

But _this_ URL gets extremely confusing if you return a `404`:

`GET https://myapi.com/v1/users?state=FL&lastName=Davis`

Does the `404` from this URL happen because there are no users, in Florida, with the last name of Davis?  Or does the `404` happen _because there is no endpoint at this address_???  There's no way to be absolutely sure.

REST also becomes a nightmare when the designers are super-duper anal retentive about ensuring that every single entity can _only_ be returned under its own endpoint.

For example, consider this possible return from the `/v1/users` endpoint:

```javascript
{
  user: {
    firstName: "Adam",
    lastName: "Davis",
    addresses: [
      {
        street: "101 Main Street",
        city: "Palookaville",
        state: "Idaho",
        postalCode: 32211
      },
      {
        street: "102 State Street",
        city: "Mainville",
        state: "Illinois",
        postalCode: 42218
      },
      {
        street: "103 Baluga Street",
        city: "Fishville",
        state: "Maine",
        postalCode: 53319
      }	 	  
    ]
  }
}
```

There are plenty of "REST Acolytes" who would swear that this data model is "wrong" for a REST endpoint.  They'll yell you down with the idea that the `addressses` should absolutely be _their own endpoint_.  

And I'm not telling you that it's "wrong" to create a standalone `address` endpoint.  But if the _only_ reason you're creating that endpoint is to satisfy some nagging inner REST purity-check, then... you should think carefully about what you're doing.

Let me put this another way:

If the addresses _only_ make sense in the context of their users, then creating a separate `address` endpoint may be an unnecessary headache - _and_ it will force your users to make more calls against your endpoints and further stress your web servers.

Additionally, endpoint designers too-often assume that data can only live under **_one_** endpoint.  For example, in the data set shown above, they'll demand that addresses are _only_ returned under a standalone `address` endpoint **_OR_** they are _only_ returned under the `users` endpoint.  

But life doesn't always have to be so rigid.  There's nothing theoretically wrong with the idea that there may be an `address` endpoint **_and_** addresses may be returned whenever you query a particular `user`.
<br/><br/>

## Conclusion
APIs should be a _service_ to your users.  They should help technically savvy users to leverage and extend your functionality.  If you make them jump through an inordinate number of hoops, you undermine the whole purpose for the API to exist at all.