# Spot: describing an API contract in TypeScript instead of YAML

> Spot takes the OpenAPI idea of a checked-in API contract and expresses it in TypeScript syntax, so the file your team reviews in a pull request is checked by the editor you already use, and the same source generates OpenAPI, Swagger and JSON Schema.

**airtasker/spot** — Spot is a concise, developer-friendly way to describe your API contract.

- Repository: https://github.com/airtasker/spot
- Stars: 567 · Forks: 37
- Language: TypeScript
- License: NOASSERTION
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/airtasker-spot

## What a Spot contract looks like

Spot, short for Single Point Of Truth, is a concise, developer-friendly way to describe an API contract, and it does that by leveraging TypeScript syntax. The example in the project overview defines a single POST endpoint to create a user, and the shape is worth reading closely because it is the whole API surface in miniature. An api decorator names the API on an empty class, an endpoint decorator gives the method and the path on another class, and request and response decorators mark the methods inside it with the body type. The request and response bodies are then declared as ordinary TypeScript interfaces. Nothing here is a special object, so the whole file is type checked and the editor can autocomplete the field names inside a body type.

## The example contract, in full

Here is the complete definition from the overview, which is also the fastest way to see what a Spot contract costs you.

```typescript
import { api, endpoint, request, response, body } from "@airtasker/spot";

@api({
  name: "My API"
})
class Api {}

@endpoint({
  method: "POST",
  path: "/users"
})
class CreateUser {
  @request
  request(@body body: CreateUserRequest) {}

  @response({ status: 201 })
  response(@body body: CreateUserResponse) {}
}

interface CreateUserRequest {
  firstName: string;
  lastName: string;
}

interface CreateUserResponse {
  firstName: string;
  lastName: string;
  role: string;
}
```

One point is worth drawing out, because it is easy to miss. You do not need to use TypeScript in your codebase to benefit from using Spot. The contract file is a TypeScript file for the sake of the tooling, and the artefact it produces is an OpenAPI document or a JSON Schema that anything else can consume.

## The argument against OpenAPI as the source of truth

The project explains why it was built, and the reason is about developer experience rather than about the specification. OpenAPI documents are stored as YAML files following a very specific schema, and you will not know that you used the wrong field name or forgot to wrap a type definition into a schema object unless you run a good OpenAPI linter. Most developers who are not intimately familiar with the OpenAPI specification end up using a visual editor such as Swagger Editor or Stoplight. Since Spot leverages TypeScript syntax, all you need to write is valid TypeScript code, your editor tells you immediately when the code is invalid and what is missing, and you get autocomplete for free. The project is candid that any typed language would have served, and that TypeScript simply happened to be one of the most concise and ubiquitous for the team that built it.

## Contracts in Git, and why a YAML document is hard to review

The second reason is about review. The project believes API contracts should be checked into Git, and that they should be systematically peer reviewed, because it is far too easy for a backend engineer to incorrectly assume what client engineers expect from an endpoint. The difficulty is structural. Because of their complex nested structure and the richness of the OpenAPI specification, OpenAPI documents can be difficult to review in a pull request, and the phrasing in the source is that they are great for machines but not always for humans. Spot aims to be as human-readable as possible, and the team reports that developers became a lot more engaged in discussions on pull requests for Spot contracts than they had been for their previous OpenAPI documents.

## Generating every format from one source

The third reason is interoperability. Depending on what you are trying to achieve, whether testing, documentation or client code generation, you will find tools that only work with OpenAPI 2, known as Swagger, and newer tools that only support OpenAPI 3, and you may also find tools for a different ecosystem such as JSON Schema or API Blueprint. Spot is built with that in mind, and instead of making you juggle various API format converters it generates every major API document format, which is where the name comes from. The generator is chosen at invocation time with a generator flag and a language flag, and the output path with an out flag.

## Installing it and running a generator

The requirements are either Docker, or Node.js 22.13.0 or newer with pnpm, which is recommended, or npm. With pnpm installed and initialised, adding the package to your project is one command.

```sh
pnpm add @airtasker/spot
```

Then you pass the definition to a generator.

```sh
pnpm exec spot generate --contract api.ts --generator openapi3 --language yaml --out doc/output
```

The package publishes a bin entry named spot, and the CLI is built with oclif, which is what gives it the help and error handling you would expect from a modern command line tool. The repository itself is a pnpm workspace with a cli/ directory, a lib/ directory for the library, a docs/ directory, a test-fixtures/ directory, an index.ts at the root for the in-memory API, and separate jest configurations for the standard run and for CI.

## The Docker image, and its quiet failure modes

Spot ships two ways, built from the same source at the same version. The npm package is the distribution available to everyone, and the Docker image needs no Node toolchain and no node_modules beside your contracts, which suits a repository that is not otherwise a Node project. The image is private to Airtasker, so outside the organisation the npm package is the route. The invocation is a bind mount of the working directory.

```sh
docker run --rm --user "$(id -u):$(id -g)" \
  --volume "$PWD:$PWD" --workdir "$PWD" \
  ghcr.io/airtasker/spot:<version> \
  generate --contract api.ts --generator openapi3 --language yaml --out doc/output
```

Each argument is doing something, and the project documents a table of why. The volume mounts the workspace at its real absolute path, because Spot passes path arguments to the filesystem unresolved, so mounting elsewhere makes relative paths resolve differently inside and outside the container. The workdir is what makes a relative contract path and output path mean what they mean at your shell. The user flag makes the output owned by you rather than by the image's user, which is load-bearing wherever a build system reads Spot's output, since an mtime cache or an incremental build input on a file you cannot overwrite breaks. The project also documents quiet failure modes, where an out path outside the mount is written inside the container and the command still exits 0.

## Conclusion

Spot fits a TypeScript team that keeps its API contract in version control and reviews it in pull requests, because the contract is valid TypeScript and the editor catches mistakes the way it catches any other type error. It does not fit a team that wants to read or edit the contract without a Node toolchain, unless you use the Docker image, which is private to Airtasker, and it does not fit a shop whose contract already lives in OpenAPI and has tooling around that format, because Spot is a replacement for that document rather than a converter for it. Before you adopt it, read the Spot Guide and the Spot Syntax pages, since the whole value depends on the syntax being learnable, and pin the contract and the Spot version together, because the generated documents change when either does.

## FAQ

### What is Spot and what does it generate?

Spot, short for Single Point Of Truth, is a developer-friendly way to describe an API contract using TypeScript syntax, and it can generate the other API contract formats you need, including OpenAPI, Swagger and JSON Schema, from that one source.

### Do I need TypeScript in my project to use Spot?

No. The project states that you do not need to use TypeScript in your codebase to benefit from using Spot. The contract file is a TypeScript file for the sake of the tooling, and what it produces is a document other tools consume.

### How do I install Spot and generate an OpenAPI document?

With Node.js 22.13.0 or newer and pnpm, add the package with pnpm add @airtasker/spot, then run pnpm exec spot generate with the --contract, --generator, --language and --out arguments to point at the contract file, pick the generator and output format, and choose where the result goes.

### What is the Spot Docker image for, and what should I watch out for?

The image lets you run Spot without a Node toolchain or node_modules beside your contracts, but it is private to Airtasker, so outside the organisation the npm package is the route. The documentation warns that path arguments are passed unresolved, so the workspace must be mounted at its real absolute path, and that the user flag matters because output owned by the wrong user breaks build systems that key caches on its mtime.

## Sources

- [airtasker/spot on GitHub](https://github.com/airtasker/spot)
- [Issues](https://github.com/airtasker/spot/issues)
- [README](https://github.com/airtasker/spot/blob/master/README.md)
- [Releases](https://github.com/airtasker/spot/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/airtasker-spot
