|
| 1 | +# Generating server stubs with TypeSpec: a user guide |
| 2 | + |
| 3 | +This guide will walk you through the process of generating server stubs from your TypeSpec API specifications, enabling you to quickly scaffold and develop your backend services. |
| 4 | + |
| 5 | +Think of it like building a house. You could ask a builder to build a house for you without any specific instructions, but the result might not be what you had in mind. However, if you provide the builder with a detailed blueprint, they can create a house that matches your vision. **In this analogy, TypeSpec is the blueprint for your API, and the server stub code is the foundation. With this blueprint and foundation in place, you can more efficiently build the rest of your service.** |
| 6 | + |
| 7 | +TypeSpec plays a vital role in your project. By providing a well-defined blueprint and a solid foundation for your API design, TypeSpec helps make the development process more efficient, consistent, and structured. |
| 8 | + |
| 9 | +**Key benefits:** |
| 10 | + |
| 11 | +* **Accelerated development:** Jumpstart your server implementation with automatically generated models and controllers. |
| 12 | +* **API consistency:** Ensure your server implementation adheres precisely to your API specification. |
| 13 | +* **Reduced manual effort:** Eliminate repetitive coding tasks, freeing you to focus on business logic. |
| 14 | +* **Improved maintainability:** Easily update your server as your API evolves by regenerating the stubs from the updated TypeSpec definition. |
| 15 | + |
| 16 | +## Prerequisites |
| 17 | + |
| 18 | +Before you begin, make sure you have the following installed: |
| 19 | + |
| 20 | +* **Node.js:** (version 20.x or later). Download from [https://nodejs.org/](https://nodejs.org/) |
| 21 | +* **npm:** (version 6.x or later). Typically included with Node.js. |
| 22 | +* **TypeSpec compiler:** Install the latest `@next` version globally using: |
| 23 | + |
| 24 | + ```bash |
| 25 | + npm install -g @typespec/compiler@next |
| 26 | + ``` |
| 27 | +* **Language-specific SDKs (optional):** Depending on the target language of your server, you may need the appropriate SDK. For example, to emit .NET SDKs, install the [.NET SDK](https://dotnet.microsoft.com/download). For Java, install the [JDK](https://www.oracle.com/java/technologies/downloads/). |
| 28 | + |
| 29 | +## Workflow options |
| 30 | + |
| 31 | +You can use the Server Stub Generation feature from TypeSpec in two ways: |
| 32 | + |
| 33 | +1. **Command-line interface (CLI):** Ideal for automated builds and developers comfortable with the command line. |
| 34 | +2. **Visual studio code (VSCode) IDE with the TypeSpec extension:** Provides a seamless, integrated development experience within VSCode. |
| 35 | + |
| 36 | +## Configuring `tspconfig.yaml` for server stub generation |
| 37 | + |
| 38 | +The `tspconfig.yaml` file is the heart of your TypeSpec project. It defines how your TypeSpec code is compiled and what outputs are generated. To enable server stub generation, you need to configure the `emit` and `options` sections of this file. |
| 39 | + |
| 40 | +Here's an example `tspconfig.yaml` file that enables server stub generation for both C# (ASP.NET Core) and JavaScript (Node.js/Express): |
| 41 | +
|
| 42 | +```yaml |
| 43 | +emit: |
| 44 | + - "@typespec/openapi3" |
| 45 | + - "@typespec/http-client-csharp" |
| 46 | + - "@typespec/http-client-js" |
| 47 | + - "@typespec/http-server-js" |
| 48 | + - "@typespec/http-server-csharp" |
| 49 | +options: |
| 50 | + "@typespec/openapi3": |
| 51 | + emitter-output-dir: "{project-root}/openapi/" |
| 52 | + "@typespec/http-client-csharp": |
| 53 | + emitter-output-dir: "{project-root}/clients/csharp" |
| 54 | + "@typespec/http-client-js": |
| 55 | + emitter-output-dir: "{project-root}/clients/javascript" |
| 56 | + "@typespec/http-server-csharp": |
| 57 | + emitter-output-dir: "{project-root}/servers/aspnet/generated" |
| 58 | + "@typespec/http-server-js": |
| 59 | + emitter-output-dir: "{project-root}/servers/javascript" |
| 60 | +``` |
| 61 | +
|
| 62 | +**Explanation:** |
| 63 | +
|
| 64 | +* **`emit` section:** This section lists the *emitters* that you want to use. An emitter is a plugin that takes your TypeSpec code and generates output in a specific format (e.g., OpenAPI, C# client, JavaScript server). |
| 65 | + * `"@typespec/openapi3"`: Generates an OpenAPI 3.0 specification. |
| 66 | + * `"@typespec/http-client-csharp"`: Generates a C# client SDK. |
| 67 | + * `"@typespec/http-client-js"`: Generates a JavaScript client SDK. |
| 68 | + * `"@typespec/http-server-csharp"`: Generates a C# (ASP.NET Core) server stub. |
| 69 | + * `"@typespec/http-server-js"`: Generates a JavaScript (Node.js/Express) server stub. |
| 70 | +* **`options` section:** This section configures the emitters. For each emitter, you can specify options like the output directory. |
| 71 | + * `emitter-output-dir`: This option specifies where the generated code should be placed. The `{project-root}` variable refers to the root directory of your TypeSpec project. |
| 72 | +
|
| 73 | +**Important:** Make sure you have installed all the necessary emitters: |
| 74 | +```bash |
| 75 | +npm install -D @typespec/openapi3 @typespec/http-client-csharp @typespec/http-client-js @typespec/http-server-csharp @typespec/http-server-js |
| 76 | +``` |
| 77 | +
|
| 78 | +## Using the cli to generate server stubs |
| 79 | +
|
| 80 | +1. **Create a TypeSpec project:** If you don't already have one, create a new TypeSpec project using `tsp init`. |
| 81 | + |
| 82 | + ```bash |
| 83 | + mkdir my-api |
| 84 | + cd my-api |
| 85 | + tsp init |
| 86 | + ``` |
| 87 | + |
| 88 | + During `tsp init`, you will be prompted to select different emitters, including server and client. If you have the necessary dependencies installed, TypeSpec can automatically populate your `tspconfig.yaml` file. |
| 89 | + |
| 90 | +2. **Define your api:** Write your API specification in a `.tsp` file (e.g., `main.tsp`). This is where you define your models, operations, and routes. |
| 91 | + |
| 92 | +3. **Compile your TypeSpec:** Use the `tsp compile` command to generate server stubs and other artifacts based on your specification. |
| 93 | + |
| 94 | + ```bash |
| 95 | + tsp compile . |
| 96 | + ``` |
| 97 | + |
| 98 | + The compiler will output the generated server stubs to the directories specified in your `tspconfig.yaml` file (e.g., `servers/aspnet/generated` for C#). |
| 99 | + |
| 100 | +4. **Project structure:** After compiling, you will typically see the following structure: |
| 101 | + |
| 102 | + ```text |
| 103 | + \my-api |
| 104 | + \tsp-output |
| 105 | + \schema # Generated OpenAPI 3.0 spec |
| 106 | + openapi.yaml |
| 107 | + \clients # Generated Client SDK for selected language(s) |
| 108 | + \dotnet |
| 109 | + \node |
| 110 | + ... |
| 111 | + \servers # Generated Server Code |
| 112 | + \aspnet |
| 113 | + \generated # Generated code for ASP.NET |
| 114 | + \node |
| 115 | + \generated # Generated code for Node.js |
| 116 | + main.tsp |
| 117 | + tspconfig.yaml |
| 118 | + package.json |
| 119 | + ``` |
| 120 | + |
| 121 | +## Understanding the generated code structure |
| 122 | + |
| 123 | +The server stub generation process creates a basic code structure for your server application. The specific files and folders generated depend on the target language and the complexity of your API. |
| 124 | + |
| 125 | +**C# (ASP.NET core) example:** |
| 126 | + |
| 127 | +Based on a generic 'todo' style app, the generated code structure for C# will typically include: |
| 128 | + |
| 129 | +```text |
| 130 | +servers/ |
| 131 | +┣ aspnet/ |
| 132 | +┃ ┗ generated/ |
| 133 | +┃ ┣ controllers/ # Contains the base controller classes |
| 134 | +┃ ┃ ┃ ┣ CommentOpsOperationsControllerBase.cs # Base class for comment-related API operations |
| 135 | +┃ ┃ ┃ ┣ CommentsOperationsControllerBase.cs # Base class for general comment operations |
| 136 | +┃ ┃ ┃ ┣ ... (other controller base classes) |
| 137 | +┃ ┣ lib/ # Utility functions |
| 138 | +┃ ┃ ┃ ┣ ArrayConstraintAttribute.cs # Custom attributes for enforcing array constraints |
| 139 | +┃ ┃ ┃ ┣ Base64UrlConverter.cs # Utility for Base64 URL encoding |
| 140 | +┃ ┃ ┃ ┣ ... (other utility files) |
| 141 | +┃ ┣ models/ # Defines the data models (DTOs) used by the API |
| 142 | +┃ ┃ ┃ ┣ Attachment.cs # Data model representing an attachment |
| 143 | +┃ ┃ ┃ ┣ Collaborator.cs # Data model representing a collaborator |
| 144 | +┃ ┃ ┃ ┣ ... (other model files) |
| 145 | +┃ ┗ operations/ # Defines service interface |
| 146 | +┃ ┃ ┃ ┣ ICommentOpsOperations.cs # Service interface for comment-related operations |
| 147 | +┃ ┃ ┃ ┣ ICommentsOperations.cs # Service interface for general comment operations |
| 148 | +┃ ┃ ┃ ┣ ... (other operation interface files) |
| 149 | +``` |
| 150 | + |
| 151 | +* **`Controllers`**: These folders contain *base classes* for your API controllers (e.g., `CommentOpsOperationsControllerBase.cs`, `TodoItemsOperationsControllerBase.cs`). These base classes define the API endpoints and handle basic request processing. **You will need to create concrete controller classes that inherit from these base classes and implement your business logic.** |
| 152 | + |
| 153 | +* **`lib`**: This directory contains utility classes such as custom attributes used for server side validation to ensure input data adhere to the spec. |
| 154 | + |
| 155 | +* **`Models`**: These files define the data models (also known as Data Transfer Objects or DTOs) used in your API (e.g., `TodoItem.cs`, `Project.cs`). These classes represent the structure of the data being exchanged between the client and the server. |
| 156 | + |
| 157 | +* **`Operations`**: These files defines the service interface used for API operations. |
| 158 | + |
| 159 | +**Node.js/express example:** |
| 160 | + |
| 161 | +``` |
| 162 | +node/ |
| 163 | +┗ tsp-output/ |
| 164 | + ┗ @typespec/ |
| 165 | +┃ ┗ http-server-javascript/ |
| 166 | +┃ ┃ ┣ helpers/ # Contains helper functions |
| 167 | +┃ ┃ ┃ ┃ ┣ header.ts # Helper for header operations |
| 168 | +┃ ┃ ┃ ┃ ┣ multipart.ts # Helper for multipart form data |
| 169 | +┃ ┃ ┃ ┃ ┗ router.ts # Helper for routing |
| 170 | +┃ ┃ ┣ http/ # Contains http request related code |
| 171 | +┃ ┃ ┃ ┃ ┗ router.ts # Helper for setting up http router |
| 172 | +┃ ┃ ┗ models/ # Contains generated data models |
| 173 | +┃ ┃ ┃ ┣ all/ # Contains helper import all the data models |
| 174 | +┃ ┃ ┃ ┃ ┃ ┗ getitdone/ # Folder containing data models generated from getitdone namespace |
| 175 | +┃ ┃ ┃ ┃ ┃ ┗ index.ts # Helper to export the data models |
| 176 | +┃ ┃ ┃ ┗ synthetic.ts # Type spec synthetic code |
| 177 | +``` |
| 178 | + |
| 179 | +* **`Helpers`**: This folder contains helper functions to perform common operations such as setting header, and multipart operations. |
| 180 | + |
| 181 | +* **`Http`**: This folder contains code for setting up the http requests. |
| 182 | + |
| 183 | +* **`Models`**: This folder contains code and helper functions for all data models. |
| 184 | + |
| 185 | +## Cli: setting up the project and adding necessary code: |
| 186 | + |
| 187 | +Now that you have the base controller, models and service interface generated, the next step is to implement the code with a service scaffolding and business logic for a runnable server. Please follow the instruction below: |
| 188 | + |
| 189 | +* **ASP.NET webapi example (C#):** |
| 190 | + |
| 191 | + 1. Create a new ASP.NET Core Web API project: |
| 192 | + |
| 193 | + ```bash |
| 194 | + cd servers/aspnet |
| 195 | + dotnet new webapi |
| 196 | + dotnet add package Swashbuckle.AspNetCore |
| 197 | + ``` |
| 198 | + |
| 199 | + 2. Replace content of `Program.cs` with code below: |
| 200 | + |
| 201 | + ```csharp |
| 202 | + var builder = WebApplication.CreateBuilder(args); |
| 203 | +
|
| 204 | + builder.Services.AddControllers(); |
| 205 | + builder.Services.AddEndpointsApiExplorer(); |
| 206 | + builder.Services.AddSwaggerGen(); |
| 207 | +
|
| 208 | + var app = builder.Build(); |
| 209 | +
|
| 210 | + // Configure the HTTP request pipeline. |
| 211 | + if (app.Environment.IsDevelopment()) |
| 212 | + { |
| 213 | + app.UseSwagger(); |
| 214 | + app.UseSwaggerUI(); |
| 215 | + } |
| 216 | +
|
| 217 | + app.UseAuthorization(); |
| 218 | + app.MapControllers(); |
| 219 | +
|
| 220 | + app.Run(); |
| 221 | + ``` |
| 222 | +
|
| 223 | + 3. **Additional scaffolding command (ASP.NET only)**. Note that you'll have to go up one level to execute the scaffolding command. |
| 224 | +
|
| 225 | + ```bash |
| 226 | + cd .. |
| 227 | + npx hscs scaffold ./generated/ . --use-swaggerui |
| 228 | + ``` |
| 229 | +
|
| 230 | + The scaffold command will set up dependency injection for the controllers and interfaces, and also generates a swagger ui for the project. |
| 231 | +
|
| 232 | + 4. The controllers and the models that were created are just service interfaces, so you'll need to add your business logic code here. Follow the examples below: |
| 233 | +
|
| 234 | + ```csharp |
| 235 | + using Microsoft.AspNetCore.Mvc; |
| 236 | + using server.Models; |
| 237 | + using server.Operations; |
| 238 | + using System.ComponentModel.DataAnnotations; |
| 239 | +
|
| 240 | + namespace server.Controllers |
| 241 | + { |
| 242 | + [ApiController] |
| 243 | + public class CommentOpsOperationsController : CommentOpsOperationsControllerBase |
| 244 | + { |
| 245 | + private static List<Comment> _comments = new List<Comment>(); |
| 246 | +
|
| 247 | + /// <inheritdoc /> |
| 248 | + public override Task<IActionResult> CreateComment([Required] string project, [Required] string todoItem, [Required] CreateCommentRequest body) |
| 249 | + { |
| 250 | + var newComment = new Comment() |
| 251 | + { |
| 252 | + Id = Guid.NewGuid().ToString(), |
| 253 | + ProjectId = project, |
| 254 | + TodoItemId = todoItem, |
| 255 | + Body = body.Body, |
| 256 | + Created = DateTime.Now |
| 257 | + }; |
| 258 | + _comments.Add(newComment); |
| 259 | + return Task.FromResult<IActionResult>(CreatedAtAction(nameof(GetComment), new { id = newComment.Id }, newComment)); |
| 260 | + } |
| 261 | +
|
| 262 | + /// <inheritdoc /> |
| 263 | + public override Task<IActionResult> GetComment(string id) |
| 264 | + { |
| 265 | + var comment = _comments.FirstOrDefault(c => c.Id == id); |
| 266 | + if (comment == null) |
| 267 | + { |
| 268 | + return Task.FromResult<IActionResult>(NotFound()); |
| 269 | + } |
| 270 | +
|
| 271 | + return Task.FromResult<IActionResult>(Ok(comment)); |
| 272 | + } |
| 273 | + } |
| 274 | + } |
| 275 | + ``` |
| 276 | +
|
| 277 | + **Important**: if your controller file is not automatically generated as `CommentOpsOperationsController.cs`, you will have to manually add in `[ApiController]` and inherit the base class `CommentOpsOperationsControllerBase`. |
| 278 | +
|
| 279 | +* **Node.js/express example:** |
| 280 | +
|
| 281 | + [Placeholder: Detailed instructions on setting up a Node.js/Express project, integrating the generated code, and running the server will be added here.] |
| 282 | +
|
| 283 | + 1. **Create a basic express app:** Use the Express generator to create a new project: |
| 284 | +
|
| 285 | + ```bash |
| 286 | + npm install -g express-generator |
| 287 | + express my-express-app |
| 288 | + cd my-express-app |
| 289 | + npm install |
| 290 | + ``` |
| 291 | +
|
| 292 | + 2. **Install additional dependencies:** You may need additional dependencies depending on your API requirements (e.g., `body-parser` for parsing request bodies). |
| 293 | +
|
| 294 | + ```bash |
| 295 | + npm install body-parser --save |
| 296 | + ``` |
| 297 | +
|
| 298 | + 3. **Integrate generated code:** Copy the contents of the `servers/node/tsp-output/@typespec/http-server-javascript` directory into your Express project. You will likely need to adapt the generated code to fit the Express routing and middleware structure. |
| 299 | +
|
| 300 | + 4. **Configure routes:** Define your API routes in your Express app, mapping them to the generated controller functions. |
| 301 | +
|
| 302 | + 5. **Run the server:** Start the Express server: |
| 303 | +
|
| 304 | + ```bash |
| 305 | + npm start |
| 306 | + ``` |
| 307 | +
|
| 308 | +## Cli: running the project and adding necessary code: |
| 309 | +
|
| 310 | +Now that you have the base controller, models and service interface generated, the next step is to implement the code with a service scaffolding and business logic for a runnable server. Please follow the instruction below: |
| 311 | +
|
| 312 | +1. **ASP.NET webapi server (C#):** |
| 313 | +
|
| 314 | + 1. Navigate to the server directory: |
| 315 | +
|
| 316 | + ```bash |
| 317 | + cd servers/aspnet |
| 318 | + ``` |
| 319 | +
|
| 320 | + 2. Run the server: |
| 321 | +
|
| 322 | + ```bash |
| 323 | + dotnet run |
| 324 | + ``` |
| 325 | +
|
| 326 | + 3. Open your browser and navigate to `http://localhost:[PORT#]/swagger/index.html` (replace `[PORT#]` with the actual port number shown in the console output). You should see the Swagger UI, allowing you to test your API endpoints. |
| 327 | +
|
| 328 | +## Using the vscode extension |
| 329 | +
|
| 330 | +The TypeSpec Extension for VSCode offers a convenient way to generate server stubs directly from your IDE. |
| 331 | +
|
| 332 | +1. **Install the extension:** Search for "TypeSpec" in the VSCode Extensions Marketplace and install the official TypeSpec extension. |
| 333 | +
|
| 334 | +2. **Create a TypeSpec project:** |
| 335 | + * Click “Create TypeSpec Project” in the EXPLORE sidebar. |
| 336 | + * `[Placeholder: Screenshot showing "Create TypeSpec Project" in the VSCode Explorer sidebar]` |
| 337 | + * Select a project root folder. |
| 338 | + * Provide the required inputs via Quick Picks, similar to using `tsp init` on the command line. |
| 339 | +
|
| 340 | +3. **Define your api:** Write your API specification in a `.tsp` file. |
| 341 | +
|
| 342 | +4. **Generate the server stub:** |
| 343 | + * Right-click on a `.tsp` file to open the context menu. |
| 344 | + * Select “Generate from TypeSpec”. |
| 345 | + * `[Placeholder: Screenshot showing the "Generate from TypeSpec" option in the context menu]` |
| 346 | + * Select "Server Stub" from the Emitter Types. |
| 347 | + * `[Placeholder: Screenshot showing the "Emitter Types" Quick Pick with "Server Stub" selected]` |
| 348 | + * Choose the desired target language (e.g., ".NET – Generate .Net server stub by @typespec/http-server-csharp"). |
| 349 | + * `[Placeholder: Screenshot showing the language selection Quick Pick]` |
| 350 | + * TypeSpec compiler will run in the background, generating a new directory under "servers\<language>". |
| 351 | +
|
| 352 | + * `[Placeholder: Screenshot showing the generated server directory structure in VSCode]` |
| 353 | +
|
| 354 | +## Next steps |
| 355 | +
|
| 356 | +* **Add business logic:** Implement the core logic for your API endpoints within the generated server stub. **Remember that the generated controllers are base classes; you will need to create concrete classes that inherit from them and implement your business logic.** |
| 357 | +* **Configure routes:** Verify and configure the routes defined in your server project. You may need to adjust the routing configuration to match your TypeSpec definitions. |
| 358 | +* **Add data persistence:** Integrate your server with a database or other data storage solution. |
| 359 | +* **Implement authentication and authorization:** Secure your API by adding appropriate authentication and authorization mechanisms. |
| 360 | +
|
| 361 | +## Getting help |
| 362 | +
|
| 363 | +If you encounter any issues or have questions about TypeSpec and server stub generation, please consult the following resources: |
| 364 | +
|
| 365 | +**TypeSpec documentation:** |
| 366 | +
|
| 367 | + [Official TypeSpec documentation](https://typespec.io/docs/) |
| 368 | +
|
| 369 | +**TypeSpec community:** |
| 370 | +
|
| 371 | + [Join our Discord discussion](https://aka.ms/typespec/discord/) |
| 372 | +
|
| 373 | +**GitHub issues:** |
| 374 | +
|
| 375 | + [Report bugs and suggest features on the TypeSpec GitHub repository](https://github.com/microsoft/typespec/issues) |
0 commit comments