Getting Started With Basketry. Code-generation for any language | by Steve Konves | Jun, 2022

Code-generation for any language

Photo by Andrea Scully on Unsplash

Basketry is the pluggable, service-oriented code-generation pipeline for any language. Read more about Basketry and view Basketry on Github.

This guide will take you step-by-step through generating a new service, starting with the Swagger pet store example.

If you haven’t yet, go ahead and read Introducing Basketry. It gives a full explanation of what Basketry is and what types of problems it solves. It also covers all of the Basketry-related terms that will show up in the guide, such as Service Definitions, Parsers, Rules, Generators, and Intermediate Representation.

We’ll talk about installing packages and creating and running scripts. This guide assumes that you already have a working knowledge of NPM or Yarn and know how to use your preferred package manager to perform those actions.

Lastly, we will be building our service in Typescript. This guide assumes that you are either familiar with setting up a new project or have access to an existing project. If you want to use the Typescript project scaffolding tool that I personally use for new projects, check out my Yeoman generator.

Get a Service Definition

Let’s start by downloading an existing Service Definition. Swagger.io offers an example Swagger doc defining an example pet store service: https://petstore.swagger.io/v2/swagger.json. Download that file as swagger.jsonand put it in the root folder of your project.

Install packages

Start by installing the following packages:

  • basketry
  • @basketry/swagger-2
  • @basketry/typescript

The first package contains the Basketry CLI via the basketry command. There are two ways to use the CLI. First, you can pass all of your project options directly to the CLI as arguments. See the README in the repository for details on these arguments. Secondly, you can add a basketry.config.json file in the root of your project, and then just run basketry without any arguments. We will be using a configuration file in this guide.

The second package is the Parser for Swagger 2.0 documents. This package exports a default module that takes the raw Swagger doc and outputs an Intermediate Representation (IR) of the service.

The last package is the Generator that takes the IR and outputs a types.ts file that contains all of the interfaces, types, and enums necessary to implement the pet store service.

Create scripts

Create the following scripts in package.json to run the Basketry CLI:

{
"scripts": {
"basketry": "basketry",
"watch:basketry": "basketry --watch"
}
}

When we run the basketry script, the Basketry CLI will look for a basketry.config.json file to know what parser and generator to use and where to find the Swagger doc. Let’s create that next.

Create a config file

Add the following basketry.config.json file to the root of your project:

{
"source": "swagger.json",
"parser": "@basketry/swagger-2",
"generators": ["@basketry/typescript"],
"output": "src/petstore"
}

Let’s walk through what each of those settings does.

First, source lets Basketry know where we want to load our Service Definition from. If you want to rename swagger.json to something else, make sure to also reflect that name change in the config.

Next, parser lets Basketry know what module to use to parse the Swagger doc into IR. The rest of the pipeline doesn’t know that the original Service Definition Language (SDL) was Swagger—the Generators (and Rules when we add them) can work with any source language. For this to work, we need to pick a Parser that understands the SDL, which is why we installed @basketry/swagger-2.

moving on, generators lets Basketry know how to convert the IR into code files. We can add as many Generators as we want. To start with, we’ll only use @basketry/typescript. We’ll add more later in the guide.

lastly, output specifies where we want Basketry to put the generated files. Some Generators may write files within subfolders; however, the output folder will be the root directory for all generated content.

Refer to the Basketry wiki for more information about config files.

Generate some code!

Now run the basketry command:

You can see that it generates types.ts. Your project will now contain (at least) the following files:

basketry-example/
├─ node_modules/
├─ src/
│ ├─ petstore/
│ │ ├─ v1/
│ │ │ ├─ types.ts
│ ├─ .gitattributes
├─ basketry.config.json
├─ package.json
├─ swagger.json

Note that types.ts is inside of a v1 folder. This is because the Swagger doc has a version of 1.0.6 (see the info object) and Basketry keeps track of the major version of source services. As we see here, @basketry/typescript used that version number to write files into version-specific folders.

And it’s that easy! At this point, we have a Swagger doc that describes our pet store, and then we setup Basketry to generate Typescript types for our service.

Design guidelines help provide consistency within a single service but also across multiple services owned and maintained by multiple teams. This consistency improves developer experience and facilitates incremental change as APIs grow and mature.

Basketry Rules provide an easy way to add this facet of API governance into your project.

Add a standard rule

Basketry offers a set of standard rules to get you started.

First, install the package @basketry/rules.

Next, let’s add the “string ID” rule to our config file:

{
"source": "swagger.json",
"parser": "@basketry/swagger-2",
"rules": ["@basketry/rules/lib/string-id"],
"generators": ["@basketry/typescript"],
"output": "src/petstore"
}

Now, when we run the basketry script, we see the following output:

This output lets us know that we have five places where our Swagger doc violates the “basketry/string-id” rule. The error output shows what rule has been violated, the file path, and location of the violation to help us make the fix.

For context, the “string ID” rule requires that any property named id must be a string. (This rule would typically be used to require non-sequential IDs for security reasons.)

Now that we found some violations, let’s go back to the Swagger doc, find the first violation, and change

{ "type": "integer", "format": "int64" }

to:

{ "type": "string", "minLength": 12 }

and re-run the basketry script:

As you can see, we fixed the first violation and our types.ts file has been updated to reflect the change.

If we make the same change to the remaining ID properties, we should see all of our violations cleared!

Set rule options

Let’s add the description rule from the standard set to ensure that we add descriptions to everything:

{
"source": "swagger.json",
"parser": "@basketry/swagger-2",
"rules": [
"@basketry/rules/lib/string-id",
"@basketry/rules/lib/type-description"
],
"generators": ["@basketry/typescript"],
"output": "src/petstore"
}

That’s a lot of errors!

We can pass some options to rules to modify their behavior in certain ways:

{
"source": "swagger.json",
"parser": "@basketry/swagger-2",
"rules": [
"@basketry/rules/lib/string-id",
{
"rule": "@basketry/rules/lib/description",
"options": { "severity": "warning" }
}
],
"generators": ["@basketry/typescript"],
"output": "src/petstore"
}

Now, all of the “description” violations are reported as warnings instead of errors. Various rules allow different options, so check out the Basketry Rules for the full set of available rules and their respective options.

You can always refer to the Basketry wiki for more docs and guides.

Leave a Comment