Skip to content

OpenAPI code generators

Andriy Mykulyak edited this page Feb 26, 2023 · 8 revisions

Introduction

This document explains how OpenAPI code generators work.

In api-tools, OpenAPI code generation facility consists of a CLI package, @fresha/codegen-openapi-cli, and a number of concrete generators, each producing source code for specific language, framework etc. CLI tool serves as an entry point, and generator packages expose themselves as commands in the CLI.

CLI

@fresha/openapi-codegen-cli exposes a binary script fresha-openapi-codegen, which is the primary interaction point for developers. After installing

$ npm install @fresha/openapi-codegen-cli

CLI script can be invoked as:

$ npx fresha-openapi-codegen <GENERATOR_COMMAND> <OPTIONS>

where <GENERATOR_COMMAND> and <OPTIONS> are generator name and options, correspondingly. By default, CLI comes with 6 bundled generators:

  • server-elixir - generates code for Elixir/Phoenix applications
  • server-nestjs - generates code for NestJS applications
  • server-mock - (experimental) generates code for mock servers based on Mirage.js
  • client-fetch - generates client code using Web Fetch API
  • docs-json-api - (experimental) generates documentation for a JSON:API-compatible API

Concrete generators

Each concrete generator type resides in a dedicated package. For example, NestJS generator resides in @fresha/openapi-codegen-server-nestjs.

Each generator package exposes one or more CLI commands, precisely (yargs command)[https://yargs.js.org/docs/#api-reference-commandcmd-desc-builder-handler], which is later registered and exposed by fresha-openapi-codegen.

Generator packages should be named like @fresha/openapi-codegen-TYPE-SUBTYPE, where TYPE should be either server or client, and SUBTYPE should indicate the tech stack the generator targets. For example, openapi-codegen-client-fetch means that it's a generator producing client-side code using fetch API.

Generator design

Each generator has a few main parts:

  • CLI command module (see yargs docs for more details), which should be located in src/command.ts. It is used by @fresha/openapi-codegen-cli to run a generator from command line
  • root generator, usually located in parts subfolder
  • zero or more partial generators, also located in the parts subfolder. A rule of thumb should be that every generator is responsible for generating content of a single source file
  • sometimes, it helps to use a context object, providing access to e.g. file system services. For testing the context should be created by helper function; for production, it should be created in CLI command module

Code generation can be split into two phases:

  • data collection, when generators extract relevant pieces of information from OpenAPI schema and existing source code (e.g. project manifest)
  • code generation, when new code has been generated based on data collected during the previous phase

Existing generators reflect this split, they all have methods collectData and generateCode. It's a rule of thumb that generateCode shouldn't accept any arguments.

Differences from Swagger codegen

Swagger has developed a great set of tools. However, they don't fit well into our environment. I needed to change several basic assumptions for api-tools code generators:

  1. Generator packages are not restricted to using text-based templates (and templates in general). For example, JavaScript / TypeScript generators use AST-based generation extensively, via ts-morph. For other languages, similar packages may be created.
  2. Swagger UI model for generators concentrates on entities from OpenAPI schema (e.g. operation, request, response, etc.). api-tools generators concentrate on entities from target tech stack, leaving the task of encapsulating details of OpenAPI spec to @fresha/openapi-model package. This allows the generators be structured in way that is better aligned with target technology. For example, for Elixir or NestJS these can be controllers and actions, but for e.g. Django apps they can be views. To simplify development of next code generators, I plan to extract commonly used approaches to helper packages.
  3. We explicitly support JSON:API specification
  4. We use TypeScript instead of Java as the source language. This makes it easier to create cross-platform applications (including e.g. web- and desktop- based clients)
  5. We explicitly target OpenAPI 3.x and newer, skipping older versions
  6. We prefer using smaller and more focused code generators. In practice this means:
    • using separate code generators for the client-side code and for the documentation
    • using generators as part of larger workflows. For example, when building client libraries one may use the generator to create some of the source code, and another templating tool to create the rest of package files
Clone this wiki locally