As we approach the summer holiday, I thought of wrapping up the series o with a deep dive into the crafting of API definitions with a example for retail domains. We'll explore the powerful capabilities of Microsoft TypeSpec and its transformation to OpenAPI. Let's go!
Understanding TypeSpec
Microsoft's TypeSpec is a robust language-agnostic tool that allows us to define data models and types. It helps to enforce type-safe design, minimise potential errors, and simplify API maintenance. Its main purpose is to provide a way of defining types and data structures that can be understood and shared across different programming languages.
The crux of TypeSpec is to allow developers to define their data models in one place, keeping them synchronised, less prone to errors, and easy to maintain. TypeSpec creates a "source of truth" for your data models and types.
But be careful, TypeSpec is still under development. The version of the TypeSpec compiler used in this introduction is 0.45.2
.
Crafting Retail Models with TypeSpec
In the context of a retail API, these models may represent customers, products, orders, and more. Defining these models accurately is crucial for efficient API functioning.
For instance, the TypeSpec model for a customer could look like this:
1model Customer {
2 id: string;
3 firstName: string;
4 lastName: string;
5 email: string;
6 phone: string;
7}
We can also have a representation of Problem+JSON standardisation of error representation in an API description.
1@error
2model ProblemDetails {
3 type: string;
4 title: string;
5 status: integer;
6 detail: string;
7}
In case of reusability these two models are separated in different tsp
files in a models
folder. This gives us the possibilities to import the models into the TypeSpec definition.
But, to start with this definition, we need to prepare our workplace a little.
First of all, we need the TypeSpec compiler.
1npm install -g @typespec/compiler
We also need to install the VSCode extensions.
1tsp code install
We then create a folder called customer_http
. In this folder we initialise TypeSpec.
1tsp init
To do this, we use the Generic Rest API
template. We select @typespec/openapi3
as the library. Then we need to install the dependencies. This gives us the following structure.
1package.json 2tspconfig.yaml 3main.tsp
We describe the definition in main.tsp
and also include the models in this file. As we can see in the example.
1import "@typespec/rest";
2import "@typespec/openapi3";
3
4import "../models/customer.tsp";
5import "../models/problemdetails.tsp";
6
7using TypeSpec.Http;
8
9@doc("A service for managing customers")
10@service({
11 title: "Customer Service",
12 version: "1.0.0",
13})
14namespace CustomerService;
15
16@route("/customers")
17@tag("customers")
18@summary("A collection of customers")
19interface Customers {
20 @get list(): Customer[] | ProblemDetails;
21 @get read(@path id: string): Customer | ProblemDetails;
22 @post create(): {@statusCode _: 201 | 404, ...Customer} | ProblemDetails;
23 @patch update(...Customer): Customer | ProblemDetails;
24 @delete delete(@path id: string): Customer | ProblemDetails;
25}
From TypeSpec to OpenAPI
Once we've crafted our TypeSpec models, we can transform them into other formats. One of the most popular choices for API specifications is OpenAPI.
We translate the TypeSpec definition into OpenAPI one by using tsp compile .
. This creates a openapi.yaml
file in a tsp-output
folder. Following we can have a look at the transformed OpenAPI definition.
1openapi: 3.0.0
2info:
3 title: Customer Service
4 version: 1.0.0
5 description: A service for managing customers
6tags:
7 - name: customers
8paths:
9 /customers:
10 get:
11 tags:
12 - customers
13 operationId: Customers_list
14 parameters: []
15 responses:
16 '200':
17 description: The request has succeeded.
18 content:
19 application/json:
20 schema:
21 type: array
22 items:
23 $ref: '#/components/schemas/Customer'
24 x-typespec-name: Customer[]
25 default:
26 description: An unexpected error response.
27 content:
28 application/json:
29 schema:
30 $ref: '#/components/schemas/ProblemDetails'
31 post:
32 tags:
33 - customers
34 operationId: Customers_create
35 parameters: []
36 responses:
37 '201':
38 description: >-
39 The request has succeeded and a new resource has been created as a
40 result.
41 content:
42 application/json:
43 schema:
44 $ref: '#/components/schemas/Customer'
45 '404':
46 description: The server cannot find the requested resource.
47 content:
48 application/json:
49 schema:
50 $ref: '#/components/schemas/Customer'
51 default:
52 description: An unexpected error response.
53 content:
54 application/json:
55 schema:
56 $ref: '#/components/schemas/ProblemDetails'
57 patch:
58 tags:
59 - customers
60 operationId: Customers_update
61 parameters: []
62 responses:
63 '200':
64 description: The request has succeeded.
65 content:
66 application/json:
67 schema:
68 $ref: '#/components/schemas/Customer'
69 default:
70 description: An unexpected error response.
71 content:
72 application/json:
73 schema:
74 $ref: '#/components/schemas/ProblemDetails'
75 requestBody:
76 required: true
77 content:
78 application/json:
79 schema:
80 $ref: '#/components/schemas/CustomerUpdate'
81 /customers/{id}:
82 get:
83 tags:
84 - customers
85 operationId: Customers_read
86 parameters:
87 - name: id
88 in: path
89 required: true
90 schema:
91 type: string
92 responses:
93 '200':
94 description: The request has succeeded.
95 content:
96 application/json:
97 schema:
98 $ref: '#/components/schemas/Customer'
99 default:
100 description: An unexpected error response.
101 content:
102 application/json:
103 schema:
104 $ref: '#/components/schemas/ProblemDetails'
105 delete:
106 tags:
107 - customers
108 operationId: Customers_delete
109 parameters:
110 - name: id
111 in: path
112 required: true
113 schema:
114 type: string
115 responses:
116 '200':
117 description: The request has succeeded.
118 content:
119 application/json:
120 schema:
121 $ref: '#/components/schemas/Customer'
122 default:
123 description: An unexpected error response.
124 content:
125 application/json:
126 schema:
127 $ref: '#/components/schemas/ProblemDetails'
128components:
129 schemas:
130 Customer:
131 type: object
132 properties:
133 id:
134 type: string
135 firstName:
136 type: string
137 lastName:
138 type: string
139 email:
140 type: string
141 phone:
142 type: string
143 required:
144 - id
145 - firstName
146 - lastName
147 - email
148 - phone
149 CustomerUpdate:
150 type: object
151 properties:
152 id:
153 type: string
154 firstName:
155 type: string
156 lastName:
157 type: string
158 email:
159 type: string
160 phone:
161 type: string
162 ProblemDetails:
163 type: object
164 properties:
165 type:
166 type: string
167 title:
168 type: string
169 status:
170 type: integer
171 detail:
172 type: string
173 required:
174 - type
175 - title
176 - status
177 - detail
Wrapping Up
In essence, Microsoft TypeSpec provides a language-agnostic method to define models, which you can then translate into other API specifications like OpenAPI. This creates a comprehensive and flexible approach to API development, fitting various use cases and technology stacks.
As I bid adieu for the summer, I leave you with the excitement of exploring TypeSpec and its incredible capabilities. Here's to a summer of innovation and relaxation! Stay tuned, and we'll see you after the break with more engaging topics from the world of APIs!
References
GitHub - danielkocot/typespec-playground-retail: A playground for TypeSpec
More articles
fromDaniel Kocot
Your job at codecentric?
Jobs
Agile Developer und Consultant (w/d/m)
Alle Standorte
More articles in this subject area
Discover exciting further topics and let the codecentric world inspire you.
Gemeinsam bessere Projekte umsetzen.
Wir helfen deinem Unternehmen.
Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.
Hilf uns, noch besser zu werden.
Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.
Blog author
Daniel Kocot
Senior Solution Architect / Head of API Consulting
Do you still have questions? Just send me a message.
Do you still have questions? Just send me a message.