Designing and developing APIs with TypeSpec

Microsoft’s Concise API Design Language has a new name and a larger role to play in building REST, OpenAPI, gRPC, and other services.

group programmers team workers collaboration
BalanceFormCreative/Shutterstock

Some time ago I wrote about the work Microsoft was doing to improve the Azure APIs. That project delivered a set of automatically generated API definitions and SDKs, making it easier to link your applications to the cloud and to manage Azure services using code.

Behind the scenes was a new language Microsoft developed called CADL, the Concise API Design Language. Building on concepts from both TypeScript and Bicep, CADL allowed you to define and describe APIs in a way that made it easy to use code to define API operations and then compile the result as an OpenAPI definition. It also let you define guardrails and common definition standards as libraries, helping architects and developers collaborate on API designs. CADL was a step up in API design, able to produce a 500-line OpenAPI definition in only 50 lines of code.

Tools like CADL are key to supporting massive platforms like Azure, which recompile their APIs multiple times a day. They need consistent APIs that users and services can consume. Modern APIs are a key dependency for client applications, for developer tools, and for much more. Ensuring that public OpenAPI definitions are correct is key to delivering the necessary support, as well allowing the host service to use automated tests and documentation generation.

That last requirement—documentation—is all-important to developers. You may not have the time necessary to write documentation, so having tools that can produce usable documentation at the same time as delivering a machine-readable set of endpoints can save everyone involved a lot of time.

From CADL to TypeSpec

In the year or so since I last wrote about CADL a lot has happened. The most obvious change is the new name, TypeSpec, which denotes its heritage in strongly-typed languages and its role in generating and maintaining API specifications. Microsoft describes TypeSpec as more than a language, instead calling it “a platform,” as it combines the language and tools needed to roll common data types, API patterns, and API guidelines into reusable components.

TypeSpec is in wide use inside Microsoft, having spread from its original home in the Azure SDK team to the Microsoft Graph team, among others. Having two of Microsoft’s largest and most important API teams using TypeSpec is a good sign for the rest of us, as it both shows confidence in the toolkit and ensures that the underlying open-source project has an active development community.

Certainly, the open-source project, hosted on GitHub, is very active. It recently released TypeSpec 0.56 and has received over 2000 commits. Most of the code is written in TypeScript and compiled to JavaScript so it runs on most development systems.

TypeSpec is cross-platform and will run anywhere Node.js runs. Installation is done via npm. While you can use any programmer’s editor to write TypeSpec code, the team recommends using Visual Studio Code, as a TypeSpec extension for VS Code provides a language server and support for common environment variables. This behaves like most VS Code language extensions, giving you diagnostic tools, syntax highlights, and IntelliSense code completion. Larger-scale projects can take advantage of a similar extension for Visual Studio.

The underlying TypeSpec language is extensible, so new API types can be quickly added, as well as support for data serialization languages. Extensions are packaged as npm files, so they can be distributed and shared using familiar tools and platforms.

The TypeSpec website includes a useful playground that allows you to experiment with a selection of different API definitions. You can quickly see how sample code compiles to an API definition, with samples including REST, HTTP, Protocol buffers, and JSON schema. The availability of alternative definitions can help with migrating from one API type to another.

It’s good to see support for Protocol buffers in TypeSpec, as these are used to define gRPC calls, which are increasingly important for delivering high-performance microservice APIs. This support should also help with cross-cloud development, as Protocol buffers are used for many Google Cloud Platform services.

Getting started with TypeSpec

Installation is straightforward, and once the TypeSpec CLI, tsp, has been downloaded, you can start to build your first API definition. The tool uses an interactive process to choose an API template and an appropriate set of libraries for the API definition you’re targeting, for example openapi3 for the current release of OpenAPI.

The next step is to install dependencies. Then you’re ready to start editing TypeSpec code, working in the main.tsp file. There’s a lot of good information in the growing documentation site, though it’s focused on working with either HTTP or OpenAPI descriptions. However, these are sufficiently generic approaches to defining APIs that you should be able to use them as guides for other API and SDK formats.

You can get a good feel for how to work with TypeSpec from building a basic REST API, delivering it as an OpenAPI description. Working with TypeSpec code will feel very familiar, as the basic syntax owes a lot to languages like C# and TypeScript. For your first API definition, start by importing the libraries for both HTTP and REST, before defining your service.

Most constructs in TypeSpec are built around decorators, specific descriptors for elements of an API specification. That requires starting at a high level and adding detail as you fill out your API. It’s an approach that works well: You start with the things you know, like service names and endpoint URLs, using parameters to add support for APIs that are designed to work in more than one region.

Namespaces bring together related elements like application names, the routes used to access specific resources, and the underlying data model. Once you have these details, you can start to add HTTP operations to your routes, and any specific calls that need to be made. Other options allow you to add parameters to a description, allowing you to define more complex API structures. This approach means you can use the same techniques for GraphQL and other HTTP-based services; you’re not limited to REST APIs.

Once you’ve written your API description, it’s time to deliver it in your chosen format. For a REST API, you can use the OpenAPI version 3 emitter, calling it when you run the TypeSpec compiler. Alternatively, your description can be added to the configuration file for your current project, where it will automatically be called when the compiler runs. Emitters are a key piece of TypeSpec, as they map your code to the API description. Microsoft provides an emitter framework you can use to speed up the development of your own emitters. Even so, creating emitters remains one of the most complex parts of the process.

Note that Microsoft provides separate documentation for using TypeSpec with Azure, as it has a public set of libraries that codify Azure’s API standards. This is aimed primarily at Microsoft’s internal users, as it suggests that using TypeSpec will help pass code reviews. What’s perhaps most useful here is a set of libraries that codify Azure standards, which can be used to help you explore TypeSpec best practices for OpenAPI definitions.

Tools like TypeSpec are an increasingly important piece of the modern development toolchain. Service-oriented architectures need well-designed and well-documented APIs, which should be defined before we start to write the code on either side of the API. So it’s good to see a tool that was originally developed to make Microsoft engineers’ lives easier getting a wider release. It will be interesting to see how a wider TypeSpec community evolves, and what other libraries and emitters the community will create.

Copyright © 2024 IDG Communications, Inc.