> ## Content Index
> Fetch the complete content index at: https://apisyouwonthate.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Distribute Spectral Style Guides with NPM
- URL: https://apisyouwonthate.com/blog/distribute-spectral-style-guides-with-npm/
- Published: 2022-06-28T00:00:00.000Z
- Updated: 2023-03-29T14:10:58.000Z
- Author: Phil Sturgeon
- Tags: #Import 2023-03-24 12:19

At some point in the growth of an organization there will come a time where  
you've got too many different APIs with random different data formats, naming  
conventions, security schemes, and generally it feels bizarre and frustrating  
trying to call any API because you cannot share any code between API consumers.  
If you're not there right now, good, you're reading this in time. If you're  
already there, yeah it sucks doesn't it, and this article will help you work  
your way out of that pit over time.

API Style Guides have always been a crucial part of API Governance, but they  
usually come in the form of a giant manifesto, written by the most  
~~opinionated~~ experienced person at your organization. Often they're ignored, and when they are read it's impossible to remember all the decisions, especially as some may change over time. Now [API style guides can be automated](https://apisyouwonthate.com/blog/automated-style-guides-for-rest-graphql-grpc), and if you're describing APIs with  
[OpenAPI](https://apisyouwonthate.com/blog/theres-no-reason-to-write-openapi-by-hand) you can use excellent tools like [Stoplight Spectral](https://github.com/stoplightio/spectral/?ref=apisyouwonthate.com), which is the natural successor to the previously popular [Speccy](https://github.com/wework/speccy?ref=apisyouwonthate.com) (a tool I built at WeWork but was promptly abandoned when I left).

Both tools let you define all the rules you need for your API Style Guide in  
YAML or JSON, and distribute them via publicly via URLs, or smush them into the filesystem somehow, but Spectral offers a fantastically powerful new way of building and distributing your style guides: you can build them in JavaScript, write tests for the rules, use whatever libraries you feel like, and distribute via NPM so that you can version your API Style Guides.

To demonstrate I've created the [APIs You Won't Hate: Style Guide](https://github.com/apisyouwonthate/style-guide?ref=apisyouwonthate.com), which will help everyone  
make their APIs better using tips we've been sharing on this blog and in the  
books for the last decade.

## Create an API Style Guide in JavaScript

Let's use the JavaScript format for the ruleset instead of the JSON or YAML  
formats that people have traditionally used when working with Spectral.

```
mkdir style-guide
cd style-guide

```

Then we will need to create some dependencies.

```shell
npm install --save @stoplight/spectral-functions
npm install --save @stoplight/spectral-formats

```

If you use Spectral already, you may be familiar with the [core functions](https://meta.stoplight.io/docs/spectral/ZG9jOjExNg-core-functions?ref=apisyouwonthate.com) and  
[core formats](https://meta.stoplight.io/docs/spectral/4dec24461f3af-open-api-rules?ref=apisyouwonthate.com) it bundles with. Don't worry if not, we'll get into that in a moment.

Create a JS file that will hold your ruleset, possibly called ruleset.js or  
spectral.js, whatever you feel like calling it.

```shell
touch ruleset.js

```

In that file we're going to start building the structure for the ruleset.

```js
import { enumeration, truthy, falsy, undefined as undefinedFunc, pattern, schema } from "@stoplight/spectral-functions";
import { oas2, oas3 } from "@stoplight/spectral-formats";

export default {
  rules: {
    // ... your rules go here
  }
}

```

This is a bit more bootstrapping than if you just start writing random YAML into  
a file, but seeing as it's just plain old JavaScript you get all the benefits of  
working in your favourite IDE, which means type hinting, auto-complete, it'll  
let you know if your function is invalid, and you can import your own functions  
to power whatever rules you can image.

Let's make some rules.

```js
import { enumeration, truthy, falsy, undefined as undefinedFunc, pattern, schema } from "@stoplight/spectral-functions";
import { oas2, oas3 } from "@stoplight/spectral-formats";

export default {
  rules: {

    'api-home': {
      description: 'APIs MUST have a root path (`/`) defined.',
      message: 'Stop forcing all API consumers to visit documentation for basic interactions when the API could do that itself.',
      given: "$.paths",
      then: {
        field: "/",
        function: truthy,
      },
      severity: 'warn',
    },

    'api-home-get': {
      description: 'APIs root path (`/`) MUST have a GET operation.',
      message: "Otherwise people won't know how to get it.",
      given: "$.paths[/]",
      then: {
        field: "get",
        function: truthy,
      },
      severity: 'warn',
    },

    // Author: Phil Sturgeon (https://github.com/philsturgeon)
    'no-numeric-ids': {
      description: 'Please avoid exposing IDs as an integer, UUIDs are preferred.',
      given: '$.paths..parameters[*].[?(@property === "name" && (@ === "id" || @.match(/(_id|Id)$/)))]^.schema',
      then: {
        function: schema,
        functionOptions: {
          schema: {
            type: "object",
            not: {
              properties: {
                type: {
                  const: "integer"
                }
              }
            },
            properties: {
              format: {
                const: 'uuid'
              }
            }
          }
        }
      },
      severity: 'error',
    },
    
    // Author: Nauman Ali (https://github.com/naumanali-stoplight)
    'no-global-versioning': {
      description: 'Using global versions just forces all your clients to do a lot more work for each upgrade. Please consider using API Evolution instead.',
      message: 'Server URL should not contain global versions',
      given: "$.servers[*].url",
      then: {
        function: pattern,
        functionOptions: {
          notMatch: '/v[1-9]'
        }
      },
      formats: [oas3],
      severity: 'warn',
    }
  }
};

```

The format of these rules might look familiar to anyone used to [creating rulesets](https://meta.stoplight.io/docs/spectral/e5b9616d6d50c-custom-rulesets?ref=apisyouwonthate.com) in JSON/YAML, it's just the JavaScript version. Functions are actually referenced as functions instead of string names for functions, which removes a lot of the confusion around where custom functions live and how they're used.

Shove some callbacks in. It's JavaScript afterall!

These rules are making sure you have something showing on your API "root route" `https://example.com/api/`, making sure it's got a `GET` defined (consider using [IETF Draft DRFC: Home Documents for HTTP APIs](https://datatracker.ietf.org/doc/html/draft-nottingham-json-home?ref=apisyouwonthate.com)), and you're [not using global versioning which is a giant pain in the ass](https://apisyouwonthate.com/blog/api-evolution-for-rest-http-apis). Your milage may vary, but that's the point of writing your own style guide. This is mine. There's also a rule in there pointing out that using auto incrementing IDs in an API are generally [a pretty bad idea](https://phil.tech/2015/auto-incrementing-to-destruction/?ref=apisyouwonthate.com).

There's loads of other rules in there which you can feel free to copy and paste into a ruleset for your API Style Guide. Head over to [ruleset.ts](https://github.com/apisyouwonthate/style-guide/blob/main/src/ruleset.ts?ref=apisyouwonthate.com) and have a root around to see if you want any, just make sure you are attributing the content (I'd have used the [DBAD license](http://dbad-license.org/?ref=apisyouwonthate.com) if I didn't think I was gonna get moaned at about it).

## Publish Your Spectral Ruleset to NPM

Before I can publish to the NPM public repository I'll need to create a  
`package.json` that contains a few specific options.

```shell
npm init

```

This will ask a whole bunch of questions via CLI prompts, and you can answer  
them a little something like this:

```
package name: (style-guide) @apisyouwonthate/style-guide
version: (1.0.0)
description: Make your HTTP APIs better, faster, stronger, whether they are still being designed (API Design-First) or your organization has flopped various mismatched APIs into production and now you're thinking some consistency would be nice. Using Spectral and OpenAPI.
entry point: (index.js) ruleset.js
test command:
git repository: https://github.com/apisyouwonthate/style-guide
keywords: openapi, openapi3, openapi31, api-design
author: Phil Sturgeon <phil@apisyouwonthate.com>
license: (ISC) MIT

```

With that `package.json` created, the next step is to grab the dependencies we  
know it'll need.

```shell
npm install --save @stoplight/spectral-functions
npm install --save @stoplight/spectral-formats

```

Time to yeet this up to the public NPM repository so *everyone* can use it in all their APIs, and as a basis for their own organization-wide API Style Guides! Read the full documentation on how to [publish modules to NPM](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages?ref=apisyouwonthate.com) (and you'll need an account, etc)

```shell
npm login
npm publish --access public

```

If that's worked, you'll be able to see it up on NPM. My package is up on NPM [over here](https://www.npmjs.com/package/@apisyouwonthate/style-guide?ref=apisyouwonthate.com).

## Using an NPM published package

Using a package that's been published to NPM works the same as any using other Spectral ruleset. IIf you're working with Spectral somewhere you can install an NPM module (e.g.: [Spectral CLI ](https://meta.stoplight.io/docs/spectral/ZG9jOjI1MTg1-spectral-cli?ref=apisyouwonthate.com) or [Spectral JS](https://meta.stoplight.io/docs/spectral/eb68e7afd463e-spectral-in-java-script?ref=apisyouwonthate.com)) you can install the package with NPM in the CLI and point Spectral to the rulesets you want to use.

```shell
cd ~/src/<your-api>

npm install --save -D @stoplight/spectral-cli

npm install --save -D @apisyouwonthate/style-guide

echo 'extends: ["@apisyouwonthate/style-guide"]' > .spectral.yaml

```

Now when you run `spectral lint openapi.yaml` you'll get all my opinions being shouted at your API, and if you're using API Design First maybe it will help you avoid wasting time writing a bunch of code that you'll just have to change later.

```
/Users/phil/src/protect-earth-api/api/openapi.yaml
  18:7   warning  api-health               Creating a `/health` endpoint is a simple solution for pull-based monitoring and manually checking the status of an API.  paths
  18:7   warning  api-home                 Stop forcing all API consumers to visit documentation for basic interactions when the API could do that itself.           paths
  36:30  warning  no-unknown-error-format  Every error response SHOULD support either RFC 7807 (https://tools.ietf.org/html/rfc6648) or the JSON:API Error format.   paths./v1/orders.post.responses[401].content.application/json
  96:30  warning  no-unknown-error-format  Every error response SHOULD support either RFC 7807 (https://tools.ietf.org/html/rfc6648) or the JSON:API Error format.   paths./v1/orders/{order}.get.responses[401].content.application/json
 112:30  warning  no-unknown-error-format  Every error response SHOULD support either RFC 7807 (https://tools.ietf.org/html/rfc6648) or the JSON:API Error format.   paths./v1/orders/{order}.get.responses[404].content.application/json

```

Thanks for making Phil's API better, Phil!

## Using NPM Rulesets in Stoplight Studio & Platform

If you're using Stoplight Platform, Stoplight Studio Desktop, VSCode Spectral, etc. then you can still use the NPM-based API Style Guides, but as there's no way to get NPM involved you can load it with this *One Amazing Trick Doctors Don't Want You To Know About*.

```yaml
extends:
  - https://unpkg.com/@apisyouwonthate/style-guide@1.1/ruleset.js

```

Using Unpkg or similar means you can load the ruleset with whatever version  
specificity you want (`@1`, `@1.1`, `@1.1.2`) and it'll work anywhere that  
Spectral does.

Looking at this, you might be wondering about versioning, and that naturally  
brings up the question of testing. Both possible, and both better off as their own blog post.

## Other API Style Guides & Rulesets

More and more companies and organizations are publishing their API Style Guides as Spectral rulesets, so I've been putting them together in a [simple little repo](https://github.com/stoplightio/spectral-rulesets?ref=apisyouwonthate.com) which will one day become a "marketplace".

Here are some of the best:

- [Adidas](https://github.com/adidas/api-guidelines/blob/master/.spectral.yml?ref=apisyouwonthate.com)
- [Box](https://github.com/box/box-openapi/blob/main/.spectral.yml?ref=apisyouwonthate.com)
- [DigitalOcean](https://github.com/digitalocean/openapi/blob/main/spectral/ruleset.yml?ref=apisyouwonthate.com)
- [Team Digitale](https://github.com/teamdigitale/api-openapi-samples/blob/master/.spectral.yml?ref=apisyouwonthate.com)

Use these rules as a basis for your style guides, and make sure your APIs are  
useful before you waste time building something rubbish.

## Host API Style Guides on Stoplight

If mucking around with NPM isn't up your alley, as always Stoplight have a  
convenient hosted solution you can use that requires a bit less duct-tape and string.

Create a [Style Guide Project](https://meta.stoplight.io/docs/platform/71b92b2b5e388-style-guide-overview?ref=apisyouwonthate.com) on Platform, then you can build and share a ruleset in a GUI experience instead of writing JavaScript. You can even [import your existing Spectral rulesets](https://meta.stoplight.io/docs/platform/364e30f79daa0-create-a-style-guide?ref=apisyouwonthate.com#imported-style-guides) to be a Style Guide Project, and leave your awkward JSON/YAML rulesets behind for good.

Either way, all the APIs at your organization can use these style guides to get feedback as people are designing their APIs, improving API Governance, and wasting less time worrying about small fry so you can talk about far bigger and more important things in your API Design Review sessions.