Rewritten from scratch to incorporate feedback from eng and arch.
This commit is contained in:
Mario Guerra 2024-07-30 13:05:11 -05:00 коммит произвёл GitHub
Родитель edf272da71
Коммит da41e74c17
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
23 изменённых файлов: 2028 добавлений и 937 удалений

Просмотреть файл

@ -1,19 +0,0 @@
---
title: Getting Started with TypeSpec For Http
pagination_next: getting-started/getting-started-http/setup # Explicitly needed as its also being the category page https://github.com/facebook/docusaurus/issues/6183
---
# Getting Started with TypeSpec for HTTP
Let's create a REST API definition with TypeSpec. TypeSpec has an official HTTP API "binding" called `@typespec/http`. It's a set of TypeSpec declarations and decorators that describe HTTP APIs and can be used by code generators to generate OpenAPI descriptions, implementation code, and the like. Built on top of the HTTP library, there is the REST library `@typespec/rest` which provides some REST concepts like resources.
TypeSpec also has an official OpenAPI emitter called `@typespec/openapi3` that consumes the HTTP API bindings and emits standard [OpenAPI 3.0](https://spec.openapis.org/oas/v3.0.3) descriptions. This can then be fed into any OpenAPI code generation pipeline.
Additionally, TypeSpec includes the `@typespec/versioning` library for handling service versioning.
References:
- [HTTP library](../../libraries/http/reference)
- [REST library](../../libraries/rest/reference)
- [OpenAPI 3 emitter](../../emitters/openapi3/reference)
- [Versioning library](../../libraries/versioning/reference)

Просмотреть файл

@ -1,27 +0,0 @@
---
title: Setup
---
# Setup
:::note
Make sure to have installed the [editor extension](../../introduction/installation.md#install-the-vs-and-vscode-extensions) to get syntax highlighting and IntelliSense.
:::
1. Make a new folder somewhere.
2. Run `tsp init` and select the `Generic REST API` template.
3. Run `tsp install` to install dependencies.
4. Run `tsp compile .` to compile the initial file. You can either run `tsp compile . --watch` to automatically compile changes on save or keep running the command manually after that.
Resulting file structure:
```
main.tsp
tspconfig.yaml
package.json
node_modules/
tsp-output/
@typespec/
openapi3/
openapi.yaml
```

Просмотреть файл

@ -1,41 +0,0 @@
---
title: Service Definition and Metadata
---
# Service Definition and Metadata
A service definition in TypeSpec is a namespace that contains all the operations for the service. This namespace can carry top-level metadata such as the service name and version. TypeSpec provides several decorators to specify this metadata. While these decorators are optional, they add valuable information that can be used by tools and code generators to produce more informative and accurate outputs:
- **`@service`**: Marks a namespace as a service namespace. It accepts the following options:
- `title`: The name of the service.
- **`@server`**: (From `TypeSpec.Http`) Specifies the host of the service. It can also accept parameters to define dynamic parts of the URL.
Here's an example that uses these to define a Pet Store service:
```typespec
import "@typespec/http";
import "@typespec/rest";
import "@typespec/versioning";
using TypeSpec.Http;
using TypeSpec.Rest;
using TypeSpec.Versioning;
/**
* This is a sample Pet Store server.
*/
@service({
title: "Pet Store Service",
})
@server("https://example.com", "Single server endpoint")
namespace PetStore;
```
The `@server` decorator can take a third parameter with additional parameters as necessary:
```typespec
@server("https://{region}.foo.com", "Regional endpoint", {
/** Region name */
region?: string = "westus",
})
```

Просмотреть файл

@ -1,43 +0,0 @@
---
title: Versioning
---
# Versioning
TypeSpec includes the `@typespec/versioning` library for handling service versioning. This library allows you to define different versions of your API and manage changes over time.
Versioning is important for maintaining backward compatibility and ensuring that clients can continue to use your API as it evolves.
It may seem premature to introduce versioning before we even have a spec, but it's worthwhile to consider versioning from the beginning of your API design process to avoid breaking changes later on.
## Using the `@versioned` decorator
The `@versioned` decorator is used to mark a namespace as versioned by a provided enum. This enum describes the supported versions of the service.
Here's an example that extends our Pet Store service to include versioning:
```typespec
import "@typespec/http";
import "@typespec/rest";
import "@typespec/versioning";
using TypeSpec.Http;
using TypeSpec.Rest;
using TypeSpec.Versioning;
/**
* This is a sample Pet Store server.
*/
@service({
title: "Pet Store Service",
})
@server("https://example.com", "Single server endpoint")
@versioned(Versions)
namespace PetStore;
enum Versions {
v1: "1.0.0",
v2: "2.0.0",
}
```
We'll make use of the versioning introduced here in an upcoming section of this guide.

Просмотреть файл

@ -1,110 +0,0 @@
---
title: Resources, Routes, and Status Codes
---
# Resources, Routes, and Status Codes
## Resources and Routes
A resource is a general term for anything that can be identified by a URI and manipulated by HTTP methods. In TypeSpec, the operations for a resource are typically grouped in a route namespace. You declare a route namespace by adding the `@route` decorator to provide the path to that resource:
```typespec
@route("/pets")
namespace Pets {
}
```
Namespaces can be nested to encapsulate different levels of information. For example, you can have a `Pets` namespace that contains operations for managing pets, and a `Toys` namespace that contains operations for managing pet toys, all within the `PetStore` namespace.
Let's add a `Pets` namespace within the `Petstore` namespace, and a `Pet` model to represent unique pets. `Toys` will be added in a later section to demonstrate versioning.
```typespec
namespace PetStore {
enum Versions {
v1: "1.0.0",
v2: "2.0.0",
}
@route("/pets")
namespace Pets {
@added(Versions.v1)
model Pet {
@minLength(1)
name: string;
@minValue(0)
@maxValue(100)
age: int32;
kind: "dog" | "cat" | "fish" | "bird" | "reptile";
}
}
}
```
To define operations on this resource, you need to provide the HTTP verbs for the route using `operation` decorators. If an HTTP method decorator is not specified, then the default is `@post` if there is a body and `@get` otherwise.
## Status Codes
HTTP status codes are used to indicate the result of an HTTP request. They provide information about whether the request was successful, if there was an error, or if additional action is needed. In TypeSpec, you can use the `@statusCode` decorator to specify the status codes for your API responses.
### Common HTTP Status Codes
Here are some common HTTP status codes and their meanings:
- **200 OK**: The request was successful, and the server returned the requested resource.
- **201 Created**: The request was successful, and a new resource was created.
- **204 No Content**: The request was successful, but there is no content to return.
- **400 Bad Request**: The server could not understand the request due to invalid syntax.
- **401 Unauthorized**: The client must authenticate itself to get the requested response.
- **403 Forbidden**: The client does not have access rights to the content.
- **404 Not Found**: The server cannot find the requested resource.
- **500 Internal Server Error**: The server encountered an unexpected condition that prevented it from fulfilling the request.
### Using the `@statusCode` Decorator
The `@statusCode` decorator is used to specify the status code for a response. You can use number literal types to create a discriminated union of response types, allowing you to handle different status codes in a single operation.
Let's add a `create` operation to our `Pets` resource and use the `@statusCode` decorator to specify the status codes for a successful operation.
```typespec
@route("/pets")
namespace Pets {
@post
op create(@body pet: Pet): {
@statusCode statusCode: 201;
} | {
@statusCode statusCode: 400;
@body error: Error;
};
}
```
**Note**: This example introduces a `@body` decorator and error handling, which will be covered in detail in later sections.
### Handling Multiple Status Codes
By using discriminated unions, you can handle multiple status codes in a single operation. This allows you to provide detailed responses based on different conditions.
For example, let's add error handling to the `create` operation:
```typespec
@route("/pets")
namespace Pets {
@post
op create(@body pet: Pet): {
@statusCode statusCode: 201;
} | {
@statusCode statusCode: 400;
@body error: Error;
};
}
```
In this example:
- The `create` operation returns a `201 Created` status code when a new pet is successfully created.
- If there is a validation error, the operation returns a `400 Bad Request` status code with an error message.
By defining status codes for your API responses, you can provide clear and consistent feedback to clients, making it easier for them to understand and handle different scenarios. This helps improve the overall user experience and ensures that your API is robust and reliable.

Просмотреть файл

@ -1,60 +0,0 @@
---
title: Error Handling
---
# Error Handling
Error handling is a crucial aspect of API design. It ensures that clients receive meaningful feedback when something goes wrong, allowing them to handle errors gracefully.
Common error scenarios include validation errors, authorization errors, resource not found errors, and server errors.
## Defining Error Models
In TypeSpec, you can define custom error models to represent different types of errors. These models can include properties such as error codes, messages, and additional details.
```typespec
@error
model ValidationError {
code: "VALIDATION_ERROR";
message: string;
details: string[];
}
@error
model NotFoundError {
code: "NOT_FOUND";
message: string;
}
@error
model InternalServerError {
code: "INTERNAL_SERVER_ERROR";
message: string;
}
```
## Handling Different Types of Errors
You can handle different types of errors by defining operations that return multiple possible responses using discriminated unions.
```typespec
@route("/pets")
namespace Pets {
@post
op create(@body pet: Pet): {
@statusCode statusCode: 201;
} | {
@statusCode statusCode: 400;
@body error: ValidationError;
} | {
@statusCode statusCode: 500;
@body error: InternalServerError;
};
}
```
## Best Practices
- **Consistent Error Response Format**: Use a consistent format for all error responses to make it easier for clients to handle errors.
- **Meaningful Error Messages**: Provide clear and actionable error messages to help clients understand what went wrong and how to fix it.
- **Standard HTTP Status Codes**: Use standard HTTP status codes to indicate the type of error (e.g., 400 for validation errors, 404 for not found errors, 500 for server errors).

Просмотреть файл

@ -1,75 +0,0 @@
---
title: Path and Query Parameters
---
# Path and Query Parameters
In TypeSpec, you can specify parameters that should be passed in the URL path or as query parameters. This is done using the `@path` and `@query` decorators, respectively.
## Path Parameters
Path parameters are parts of the URL that are variable and are used to identify specific resources. They are marked with the `@path` decorator. These parameters are appended to the URL unless a substitution with that parameter name exists in the resource path.
For example, let's define a `read` operation that retrieves a specific pet by its ID:
```typespec
@route("/pets")
namespace Pets {
op read(@path petId: int32): {
@statusCode statusCode: 200;
@body pet: Pet;
} | {
@statusCode statusCode: 404;
@body error: NotFoundError;
};
}
```
In this example, `petId` is a path parameter. The resulting URL for this operation might look like `/pets/123`, where `123` is the value of `petId`.
## Query Parameters
Query parameters are used to filter or modify the results of an operation. They are marked with the `@query` decorator and are appended to the URL as key-value pairs.
For example, let's add a `list` operation that supports pagination using query parameters:
```typespec
@route("/pets")
namespace Pets {
op list(@query skip?: int32, @query top?: int32): {
@statusCode statusCode: 200;
@body pets: Pet[];
};
}
```
In this example, `skip` and `top` are query parameters. The resulting URL for this operation might look like `/pets?skip=10&top=20`, where `skip` and `top` are used to control pagination.
## Combining Path and Query Parameters
You can combine path and query parameters in a single operation. For example, let's define a `search` operation that retrieves pets by their type and supports pagination:
```typespec
@route("/pets/{type}")
namespace Pets {
model Pet {
@minLength(1)
name: string;
@minValue(0)
@maxValue(100)
age: int32;
kind: "dog" | "cat" | "fish" | "bird" | "reptile";
}
op search(@path type: string, @query skip?: int32, @query top?: int32): {
@statusCode statusCode: 200;
@body pets: Pet[];
};
}
```
In this example, `type` is a path parameter, and `skip` and `top` are query parameters. The resulting URL for this operation might look like `/pets/dog?skip=10&top=20`, where `dog` is the value of `type`, and `skip` and `top` are used for pagination.
By using the `@path` and `@query` decorators, you can clearly define how parameters should be passed in the URL, making your API more intuitive and easier to use.

Просмотреть файл

@ -1,116 +0,0 @@
---
title: Headers
---
# Headers
Headers are used to pass additional information with the request or response. Model properties and parameters that should be passed in a header use the `@header` decorator. The decorator takes the header name as a parameter. If a header name is not provided, it is inferred from the property or parameter name.
## Using the `@header` Decorator
The `@header` decorator can be used to specify headers in both requests and responses. Here are some common use cases:
- **Authorization**: Passing authentication tokens.
- **Content-Type**: Specifying the media type of the resource.
- **Custom Headers**: Any application-specific headers.
### Example: Authorization Header
Let's add an `Authorization` header to a request:
```typespec
@route("/pets")
namespace Pets {
op list(@header Authorization: string): {
@statusCode statusCode: 200;
@body pets: Pet[];
};
}
```
### Example: Content-Type Header
Let's specify the `Content-Type` header in a response:
```typespec
@route("/pets")
namespace Pets {
op create(@header Authorization: string, @body pet: Pet): {
@statusCode statusCode: 201;
@header Content-Type: "application/json";
};
}
```
In this example, the `Content-Type` header is used to specify that the response body is in JSON format.
## Example: `etag` Header
`etag` stands for "entity tag" and is a part of HTTP headers used for web cache validation and conditional requests from browsers for resources. It is a unique identifier assigned by a web server to a specific version of a resource found at a URL. If the resource content changes, a new and different `etag` is assigned.
Let's update our pet store operations to include new operations and update existing operations with relevant headers:
```typespec
@route("/pets")
namespace Pets {
op list(@header Authorization: string, @query skip: int32, @query top: int32): {
@statusCode statusCode: 200;
@body pets: Pet[];
};
op read(@header Authorization: string, @path petId: int32, @header ifMatch?: string): {
@statusCode statusCode: 200;
@header eTag: string;
@body pet: Pet;
} | {
@statusCode statusCode: 404;
@body error: NotFoundError;
};
@post
op create(@header Authorization: string, @body pet: Pet): {
@statusCode statusCode: 201;
@header Content-Type: "application/json";
} | {
@statusCode statusCode: 400;
@body error: ValidationError;
} | {
@statusCode statusCode: 500;
@body error: InternalServerError;
};
@put
op update(@header Authorization: string, @path petId: int32, @body pet: Pet): {
@statusCode statusCode: 200;
@header Content-Type: "application/json";
@body updatedPet: Pet;
} | {
@statusCode statusCode: 404;
@body error: NotFoundError;
} | {
@statusCode statusCode: 400;
@body error: ValidationError;
} | {
@statusCode statusCode: 500;
@body error: InternalServerError;
};
@delete
op delete(@header Authorization: string, @path petId: int32): {
@statusCode statusCode: 204;
} | {
@statusCode statusCode: 404;
@body error: NotFoundError;
} | {
@statusCode statusCode: 500;
@body error: InternalServerError;
};
}
```
In this example:
- The `Authorization` header is used in all operations to pass an authentication token.
- The `ifMatch` header is used in the `read` operation to specify the `etag` value that the client has. The server can then compare this value with the current `etag` of the resource.
- The `eTag` header is included in the `read` response to provide the current `etag` value of the resource.
- The `Content-Type` header is used in the `create` and `update` operations to specify that the response body is in JSON format.

Просмотреть файл

@ -1,156 +0,0 @@
---
title: Request and Response Bodies
---
# Request and Response Bodies
Request and response bodies can be declared explicitly using the `@body` decorator. This decorator helps to clearly indicate which part of the model is the body of the request or response. While it may not change the API's functionality, it provides several benefits:
1. **Clarity and readability**: Using the `@body` decorator makes it explicit which part of the model is intended to be the body. This can improve the readability of the code, making it easier for developers to understand the structure of the API.
2. **Consistency**: Applying the `@body` decorator consistently across your API definitions can help maintain a uniform style. This can be particularly useful in larger projects with multiple contributors.
3. **Tooling and documentation**: Some tools and documentation generators may rely on the `@body` decorator to produce more accurate and detailed outputs. By explicitly marking the body, you ensure that these tools can correctly interpret and document your API.
Let's revisit some of or pet store operations that use the `@body` decorator:
```typespec
@route("/pets")
namespace Pets {
model Pet {
@minLength(1)
name: string;
@minValue(0)
@maxValue(100)
age: int32;
kind: "dog" | "cat" | "fish" | "bird" | "reptile";
}
op list(@header Authorization: string, @query skip?: int32, @query top?: int32): {
@statusCode statusCode: 200;
@body pets: Pet[];
};
op read(@header Authorization: string, @path petId: int32): {
@statusCode statusCode: 200;
@body pet: Pet;
} | {
@statusCode statusCode: 404;
@body error: NotFoundError;
};
@post
op create(@header Authorization: string, @body pet: Pet): {
@statusCode statusCode: 201;
@header contentType: "application/json";
@body message: string;
} | {
@statusCode statusCode: 400;
@body error: ValidationError;
} | {
@statusCode statusCode: 500;
@body error: InternalServerError;
};
}
```
### Explanation
- **Pet Model**: The `Pet` model defines the structure of a pet with fields for `name`, `age`, and `kind`.
- `name`: A string field with a minimum length of 1.
- `age`: An integer field with a minimum value of 0 and a maximum value of 100.
- `kind`: A string field that can be one of "dog", "cat", "fish", "bird", or "reptile".
- **list Operation**: The `list` operation returns a list of pets. The response body is explicitly marked with the `@body` decorator to indicate that it contains an array of `Pet` objects.
- **read Operation**: The `read` operation retrieves a specific pet by its ID. The response body is explicitly marked with the `@body` decorator to indicate that it contains a `Pet` object.
- **create Operation**: The `create` operation creates a new pet. The request body is explicitly marked with the `@body` decorator to indicate that it contains a `Pet` object. This means that when a client sends a request to create a new pet, the request body must include the `name`, `age`, and `kind` fields as defined in the `Pet` model.
### Example Request for Create Operation
Here is an example of what the request body might look like when creating a new pet:
```json
{
"name": "Buddy",
"age": 5,
"kind": "dog"
}
```
This JSON object matches the structure of the `Pet` model, with fields for `name`, `age`, and `kind`.
### Implicit vs. Explicit `@body`
Note that in the absence of an explicit `@body`:
1. **Request Body**: The set of parameters that are not marked with `@header`, `@query`, or `@path` will automatically form the request body.
2. **Response Body**: The set of properties of the return model that are not marked with `@header`, `@query`, or `@path` will automatically form the response body.
3. **Non-Model Return Types**: If the return type of an operation is not a model (e.g., a primitive type like `string` or `int32`), then that return type itself defines the response body. For example, if an operation returns a `string` without using the `@body` decorator, the `string` will be the response body.
Here's an example to illustrate these points:
```typespec
@route("/example")
namespace Example {
op implicitBody(param1: string, param2: int32): string {
// param1 and param2 form the request body
// The return type (string) forms the response body
}
}
```
In this example:
- `param1` and `param2` are not marked with `@header`, `@query`, or `@path`, so they automatically form the request body.
- The return type is `string`, so the response body is the `string` itself.
### `@bodyRoot` Decorator
The `@bodyRoot` decorator is used when you want to specify that the entire body of the request or response should be a single value, rather than an object with multiple properties. This is useful when the body is a primitive type or a single model instance.
#### Example
Let's revisit some of our pet store operations that can benefit from using the `@bodyRoot` decorator:
```typespec
@route("/pets")
namespace Pets {
@post
op create(@header Authorization: string, @bodyRoot pet: Pet): {
@statusCode statusCode: 201;
@header contentType: "application/json";
@bodyRoot message: string;
} | {
@statusCode statusCode: 400;
@bodyRoot error: ValidationError;
} | {
@statusCode statusCode: 500;
@bodyRoot error: InternalServerError;
};
op read(@header Authorization: string, @path petId: int32): {
@statusCode statusCode: 200;
@header eTag: string;
@bodyRoot pet: Pet;
} | {
@statusCode statusCode: 404;
@bodyRoot error: NotFoundError;
};
}
```
In this example:
- The `@bodyRoot` decorator is used to indicate that the entire request body for the `create` operation is the `pet` parameter.
- The `@bodyRoot` decorator is also used to indicate that the entire response body for the `create` and `read` operations is either the `message`, `pet`, or `error` value.
### Recommendation
While TypeSpec can infer the request and response bodies in the absence of an explicit `@body` decorator, this can sometimes be confusing for developers. Therefore, it is recommended to use the `@body` decorator explicitly to clearly indicate which part of the model is intended to be the body. This improves the readability and maintainability of your API definitions.
Similarly, use the `@bodyRoot` decorator when the entire body should be a single value. This makes it clear that the body is not an object with multiple properties, but a single entity, improving clarity and consistency in your API definitions.

Просмотреть файл

@ -1,30 +0,0 @@
---
title: Interfaces
---
# Interfaces
In TypeSpec, an interface is a way to define a set of operations that can be grouped together. Interfaces are particularly useful when you want to apply the same set of operations to different resources or when you want to use automatic route generation, covered in the next section.
## Defining an Interface
Let's define an interface for managing pet toys. Since we've decided to extend our pet store service to support toys, we'll need to version the service. We'll add support for toys in version 2 of our pet store service:
```typespec
@added(Versions.v2)
interface ToyOperations {
@get
getToy(@path petId: int32, @path toyId: int32): Toy | NotFoundError;
@put
updateToy(@path petId: int32, @path toyId: int32, @body toy: Toy): Toy | NotFoundError;
}
```
In this example, the `ToyOperations` interface defines two operations: `getToy` and `updateToy`. The `@added(Versions.v2)` decorator indicates that these operations are part of version 2 of the service. We're also making use of the `NotFoundError` defined earlier to handle cases where a toy is not found.
## Using Interfaces
Interfaces can be used to group operations that apply to different resources. This helps in maintaining a consistent structure and reusing common operations.
We'll use the `ToyOperations` interface to automatically generate routs and operations for managing pet toys in the next section.

Просмотреть файл

@ -1,76 +0,0 @@
---
title: Resources and Routes
---
# Automatic Route Generation
Automatic route generation in TypeSpec allows you to generate URL paths for your API operations automatically, reducing the need to manually specify routes. This is achieved using the `@autoRoute` decorator.
### Key Concepts
- **@autoRoute**: Automatically generates routes for operations in an interface.
- **@path**: Marks a parameter as part of the URL path.
- **@segment**: Defines the specific part of the URL path that the parameter belongs to.
### Example: Managing Pet Toys
Let's extend our Pet Store example to include operations for managing pet toys. We'll define a `Toy` model and use the `ToyOperations` interface we previously defined to generate operations for getting and updating toy information. We'll define a `CommonParameters` model to define common path parameters for both pet and toy operations.
Additionally, we'll use the `@added` decorator to indicate that these operations are part of version 2 of the service.
#### Step 1: Define Common Parameters
```typespec
model CommonParameters {
@path
@segment("pets")
petId: int32;
@added(Versions.v2)
@path
@segment("toys")
toyId: int32;
}
```
- **CommonParameters**: This model defines common path parameters for pets and toys.
- `petId`: Part of the URL segment `/pets/{petId}`.
- `toyId`: Part of the URL segment `/toys/{toyId}`.
#### Step 2: Define the Toy Model
```typespec
@added(Versions.v2)
model Toy {
name: string;
}
```
- **Toy**: This model defines the structure of a toy, with a `name` property.
#### Step 3: Extend the ToyOperations Interface to use Common Parameters
```typespec
@autoRoute
interface ToyOperations {
@added(Versions.v2)
@get
getToy(...CommonParameters): Toy | NotFoundError;
@added(Versions.v2)
@put
updateToy(...CommonParameters, toy: Toy): Toy | NotFoundError;
}
```
#### Resulting Routes
The `@autoRoute` decorator and the `CommonParameters` model will generate the following routes for the operations:
```text
/pets/{petId}/toys/{toyId}
```
By using the @autoRoute decorator in TypeSpec, you can significantly simplify the process of defining routes for your API operations. This approach not only reduces the need for manual route specification but also ensures consistency and reduces the likelihood of errors.
Automatic route generation is particularly useful when dealing with complex APIs that have multiple nested resources, as it allows you to maintain a clear and organized structure.

Просмотреть файл

@ -1,175 +0,0 @@
---
title: Complete Example
---
# Complete Example
In this tutorial, we have covered the basics of creating a REST API definition using TypeSpec. We started by setting up a new TypeSpec project and then defined a Pet Store service with various operations. We explored how to use decorators to define routes, handle path and query parameters, manage headers, and specify request and response bodies. We also looked at how to automatically generate routes, define status codes, handle errors, and manage versioning.
By following these steps, you should now have a good understanding of how to use TypeSpec to define and manage your HTTP APIs. For more advanced features and detailed documentation, refer to the official TypeSpec documentation and community resources.
Here's the complete Pet Store service definition written in TypeSpec:
```tsp tryit="{"emit": ["@typespec/openapi3"]}"
import "@typespec/http";
import "@typespec/rest";
import "@typespec/versioning";
using TypeSpec.Http;
using TypeSpec.Rest;
using TypeSpec.Versioning;
/**
* This is a sample Pet Store server.
*/
@service({
title: "Pet Store Service",
})
@server("https://example.com", "Single server endpoint")
@versioned(Versions)
namespace PetStore;
enum Versions {
v1: "1.0.0",
v2: "2.0.0",
}
@route("/pets")
namespace Pets {
@added(Versions.v1)
model Pet {
@minLength(1)
name: string;
@minValue(0)
@maxValue(100)
age: int32;
kind: "dog" | "cat" | "fish" | "bird" | "reptile";
}
op list(@header Authorization: string, @query skip?: int32, @query top?: int32): {
@statusCode statusCode: 200;
@body pets: Pet[];
};
op read(@header Authorization: string, @path petId: int32, @header ifMatch?: string): {
@statusCode statusCode: 200;
@header eTag: string;
@body pet: Pet;
} | {
@statusCode statusCode: 404;
@body error: NotFoundError;
};
@post
op create(@header Authorization: string, @body pet: Pet): {
@statusCode statusCode: 201;
@header contentType: "application/json";
@body message: string;
} | {
@statusCode statusCode: 400;
@body error: ValidationError;
} | {
@statusCode statusCode: 500;
@body error: InternalServerError;
};
@put
op update(@header Authorization: string, @path petId: int32, @body pet: Pet):
| {
@statusCode statusCode: 200;
@header contentType: "application/json";
@body updatedPet: Pet;
}
| {
@statusCode statusCode: 404;
@body error: NotFoundError;
}
| {
@statusCode statusCode: 400;
@body error: ValidationError;
}
| {
@statusCode statusCode: 500;
@body error: InternalServerError;
};
@delete
op delete(@header Authorization: string, @path petId: int32): {
@statusCode statusCode: 204;
} | {
@statusCode statusCode: 404;
@body error: NotFoundError;
} | {
@statusCode statusCode: 500;
@body error: InternalServerError;
};
// Search operation combining path and query parameters
op search(
@header Authorization: string,
@path type: string,
@query skip?: int32,
@query top?: int32,
): {
@statusCode statusCode: 200;
@body pets: Pet[];
};
}
model CommonParameters {
@path
@segment("pets")
petId: int32;
@added(Versions.v2)
@path
@segment("toys")
toyId: int32;
}
@added(Versions.v2)
model Toy {
name: string;
}
@error
model ValidationError {
code: "VALIDATION_ERROR";
message: string;
details: string[];
}
@error
model NotFoundError {
code: "NOT_FOUND";
message: string;
}
@error
model InternalServerError {
code: "INTERNAL_SERVER_ERROR";
message: string;
}
@autoRoute
interface ToyOperations {
@added(Versions.v2)
@get
getToy(...CommonParameters): Toy | NotFoundError;
@added(Versions.v2)
@put
updateToy(...CommonParameters, toy: Toy): Toy | NotFoundError;
}
```
Running `tsp compile .` will generate two versions of the OpenAPI description for this service in your `tsp-output` folder, one for each version defined in the `Versions` enum.
```
tsp-output/
@typespec/
┗ openapi3/
┃ ┣ openapi.1.0.0.yaml
┃ ┗ openapi.2.0.0.yaml
```

Просмотреть файл

@ -0,0 +1,336 @@
---
id: 01-setup-basic-syntax
title: Getting Started with TypeSpec For REST APIs
pagination_next: getting-started/getting-started-rest/02-operations-responses # Explicitly needed as its also being the category page https://github.com/facebook/docusaurus/issues/6183
---
# Getting Started with TypeSpec for REST APIs
## Introduction
Welcome to the first part of our tutorial on using TypeSpec to define REST APIs with HTTP. In this section, we'll introduce you to TypeSpec, help you set up your environment, and cover the basic syntax and structure of TypeSpec. By the end of this section, you'll have a solid foundation to build upon in the subsequent sections.
Before we start writing TypeSpec code, we need to set up our development environment. For detailed instructions on setting up your environment, please refer to the [Installation Guide](../../introduction/installation.md).
### Summary of Setup and Installation
1. **Install Node.js**: Download and install Node.js from [nodejs.org](https://nodejs.org/).
2. **Install TypeSpec CLI**: Run `npm install -g @typespec/compiler` to install the TypeSpec CLI.
3. **Verify Installation**: Run `tsp --version` to verify that the TypeSpec CLI is installed correctly.
4. **Create a New Project**:
- Run `tsp init` and select the `Generic REST API` template.
- Run `tsp install` to install dependencies.
- Run `tsp compile .` to compile the initial file. You can also run `tsp compile . --watch` to automatically compile changes on save.
## Basic Syntax and Structure
Now that we have our environment set up, let's dive into the basic syntax and structure of TypeSpec. We'll start with a simple example to illustrate the key concepts.
### Import and Using Statements
Before defining models and services, we need to import the necessary TypeSpec libraries and make them available in our namespace.
As we progress through the tutorial, you can follow along by updating the `main.tsp` file in your project and compiling the changes to see the results reflected in the generated `openapi.yaml` specification.
In most cases throughout this tutorial, you can alternatively use the `Try it` feature with the code samples to view the generated OpenAPI spec in your browser via the TypeSpec Playground.
Let's begin by adding the following import and using statements to the `main.tsp` file:
```typespec
import "@typespec/http";
using TypeSpec.Http;
```
In this example:
- `import` statement brings in the [TypeSpec HTTP library](../../libraries/http/reference/), which provides the decorators and models we'll be using to define our REST API.
- `using` statement makes the imported library available in the current namespace, allowing us to use its features and decorators.
### Understanding Models
In TypeSpec, a [model](../../language-basics/models.md) is a fundamental building block used to define the structure of data. Models are used to represent entities, such as a `Pet`, with various properties that describe the entity's attributes.
### Example: Defining a Simple Model
Let's define a simple model for a `Pet`:
```typespec
import "@typespec/http";
using TypeSpec.Http;
model Pet {
id: int32;
name: string;
age: int32;
kind: petType;
}
enum petType {
dog: "dog",
cat: "cat",
fish: "fish",
bird: "bird",
reptile: "reptile",
}
```
In this example:
- The `model` keyword is used to define a new model named `Pet`.
- The `Pet` model has four properties: `id`, `name`, `age`, and `kind`.
- The `petType` [`enum`](../../language-basics/enums.md) defines possible values for the `kind` property.
### Example: Adding Validation Annotations
We can add [validation](../../language-basics/values#validation) annotations to our model properties to enforce certain constraints:
```typespec
import "@typespec/http";
using TypeSpec.Http;
model Pet {
id: int32;
@minLength(1)
name: string;
@minValue(0)
@maxValue(100)
age: int32;
kind: petType;
}
enum petType {
dog: "dog",
cat: "cat",
fish: "fish",
bird: "bird",
reptile: "reptile",
}
```
In this example:
- `@minLength(1)` ensures that the `name` property has at least one character.
- `@minValue(0)` and `@maxValue(100)` ensure that the `age` property is between 0 and 100.
## Defining a REST Service
A REST service in TypeSpec is defined using the [`@service`](../../standard-library/built-in-decorators#@service) decorator. This decorator allows you to specify metadata about your service, such as its title. Additionally, you can use the [`@server`](../../libraries/http/reference/decorators#@TypeSpec.Http.server) decorator to define the server endpoint where your service will be hosted.
### Example: Defining a Service with a Title and Server Endpoint
Let's start by defining a simple REST service for a Pet Store:
```typespec
import "@typespec/http";
using TypeSpec.Http;
@service({
title: "Pet Store",
})
@server("https://example.com", "Single server endpoint")
namespace PetStore;
```
In this example:
- The `@service` decorator is used to define a service with the title "Pet Store".
- The `@server` decorator specifies the server endpoint for the service, which is "https://example.com".
## Organizing with Namespaces
[Namespaces](../../language-basics/namespaces.md) in TypeSpec help you organize your models and operations logically. They act as containers for related definitions, making your API easier to manage and understand.
### Example: Creating a Namespace
Let's create a namespace for our Pet Store service:
```typespec
import "@typespec/http";
using TypeSpec.Http;
@service({
title: "Pet Store",
})
@server("https://example.com", "Single server endpoint")
namespace PetStore;
```
In this example:
- The `namespace` keyword is used to define a namespace named `PetStore`.
- All models and operations related to the Pet Store service will be defined within this namespace.
## Adding Models to the Namespace
Next, we'll add the `Pet` model we defined earlier to our `PetStore` namespace.
### Example: Adding the Pet Model
```typespec
import "@typespec/http";
using TypeSpec.Http;
@service({
title: "Pet Store",
})
@server("https://example.com", "Single server endpoint")
namespace PetStore;
model Pet {
id: int32;
@minLength(1)
name: string;
@minValue(0)
@maxValue(100)
age: int32;
kind: petType;
}
enum petType {
dog: "dog",
cat: "cat",
fish: "fish",
bird: "bird",
reptile: "reptile",
}
```
In this example:
- The `Pet` model is defined within the `PetStore` namespace.
- The model includes validation annotations to enforce constraints on the properties.
- The `petType` enum is also defined within the `PetStore` namespace.
## Defining HTTP Operations
Now that we have our service, namespace, and model defined, let's add some HTTP [operations](../../language-basics/operations.md) to interact with our `Pet` model. We'll start with a simple `GET` operation to list all pets.
### Example: Defining a GET Operation
```tsp tryit="{"emit": ["@typespec/openapi3"]}"
import "@typespec/http";
using TypeSpec.Http;
@service({
title: "Pet Store",
})
@server("https://example.com", "Single server endpoint")
namespace PetStore;
model Pet {
id: int32;
@minLength(1)
name: string;
@minValue(0)
@maxValue(100)
age: int32;
kind: petType;
}
enum petType {
dog: "dog",
cat: "cat",
fish: "fish",
bird: "bird",
reptile: "reptile",
}
@route("/pets")
namespace Pets {
@get
op listPets(): {
@body pets: Pet[];
};
}
```
In this example:
- The `@route` decorator is used to define the base path for the `Pets` namespace.
- The `@get` decorator defines a `GET` operation named `listPets`.
- The `listPets` operation returns a list of `Pet` objects in the response body.
### Example: Defining a GET Operation with Path Parameter
Let's add another `GET` operation to retrieve a specific pet by its `petId`.
```tsp tryit="{"emit": ["@typespec/openapi3"]}"
import "@typespec/http";
using TypeSpec.Http;
@service({
title: "Pet Store",
})
@server("https://example.com", "Single server endpoint")
namespace PetStore;
model Pet {
id: int32;
@minLength(1)
name: string;
@minValue(0)
@maxValue(100)
age: int32;
kind: petType;
}
enum petType {
dog: "dog",
cat: "cat",
fish: "fish",
bird: "bird",
reptile: "reptile",
}
@route("/pets")
namespace Pets {
@get
op listPets(): {
@body pets: Pet[];
};
@get
op getPet(@path petId: int32): {
@body pet: Pet;
} | {
@body error: NotFoundError;
};
}
@error
model NotFoundError {
code: "NOT_FOUND";
message: string;
}
```
In this example:
- The `getPet` operation retrieves a specific pet by its `petId` and returns it in the response body.
- If the pet is not found, it returns a `NotFoundError`, a custom error we've defined within the PetStore namespace. We'll cover error handling in more detail in a later section.
## Conclusion
In this section, we introduced you to TypeSpec, set up the development environment, and covered the basic syntax and structure of TypeSpec. We defined a simple model with validation annotations, created a REST service with a title and server endpoint, organized our API using namespaces, and added a model and HTTP operations.
In the next section, we'll dive deeper into defining more HTTP operations and handling different types of responses.

Просмотреть файл

@ -0,0 +1,345 @@
---
id: 02-operations-responses
title: Operations and Responses
---
# Operations and Responses
## Introduction
In this section, we'll build upon the basics we covered in the previous section and guide you through expanding your REST API using TypeSpec. We'll define more HTTP operations and handle different types of responses.
## Defining More HTTP Operations
Now that we have a basic `GET` operation to list all pets, let's add more operations to our API. We'll add operations for creating, updating, and deleting pets.
### Example: Defining a POST Operation
Let's define a `POST` operation to create a new pet:
```tsp tryit="{"emit": ["@typespec/openapi3"]}"
import "@typespec/http";
using TypeSpec.Http;
@service({
title: "Pet Store",
})
@server("https://example.com", "Single server endpoint")
namespace PetStore;
model Pet {
id: int32;
@minLength(1)
name: string;
@minValue(0)
@maxValue(100)
age: int32;
kind: petType;
}
enum petType {
dog: "dog",
cat: "cat",
fish: "fish",
bird: "bird",
reptile: "reptile",
}
@route("/pets")
namespace Pets {
@get
op listPets(): {
@body pets: Pet[];
};
@get
op getPet(@path petId: int32): {
@body pet: Pet;
} | {
@body error: NotFoundError;
};
@post
op createPet(@body pet: Pet): {
@statusCode statusCode: 201;
@body newPet: Pet;
};
}
@error
model NotFoundError {
code: "NOT_FOUND";
message: string;
}
```
In this example:
- The `createPet` operation is defined using the `@post` decorator.
- It takes a `Pet` object in the request body and returns the created `Pet` object with a status code of 201.
### Example: Defining a PUT Operation
Let's define a `PUT` operation to update an existing pet:
```tsp tryit="{"emit": ["@typespec/openapi3"]}"
import "@typespec/http";
using TypeSpec.Http;
@service({
title: "Pet Store",
})
@server("https://example.com", "Single server endpoint")
namespace PetStore;
model Pet {
id: int32;
@minLength(1)
name: string;
@minValue(0)
@maxValue(100)
age: int32;
kind: petType;
}
enum petType {
dog: "dog",
cat: "cat",
fish: "fish",
bird: "bird",
reptile: "reptile",
}
@route("/pets")
namespace Pets {
@get
op listPets(): {
@body pets: Pet[];
};
@get
op getPet(@path petId: int32): {
@body pet: Pet;
} | {
@body error: NotFoundError;
};
@post
op createPet(@body pet: Pet): {
@statusCode statusCode: 201;
@body newPet: Pet;
};
@put
op updatePet(@path petId: int32, @body pet: Pet): {
@body updatedPet: Pet;
} | {
@body error: NotFoundError;
};
}
@error
model NotFoundError {
code: "NOT_FOUND";
message: string;
}
```
In this example:
- The `updatePet` operation is defined using the `@put` decorator.
- It takes a `petId` as a path parameter and a `Pet` object in the request body, returning the updated `Pet` object or a `NotFoundError`.
### Example: Defining a DELETE Operation
Let's define a `DELETE` operation to delete an existing pet:
```typespec
import "@typespec/http";
using TypeSpec.Http;
@service({
title: "Pet Store",
})
@server("https://example.com", "Single server endpoint")
namespace PetStore;
model Pet {
id: int32;
@minLength(1)
name: string;
@minValue(0)
@maxValue(100)
age: int32;
kind: petType;
}
enum petType {
dog: "dog",
cat: "cat",
fish: "fish",
bird: "bird",
reptile: "reptile",
}
@route("/pets")
namespace Pets {
@get
op listPets(): {
@body pets: Pet[];
};
@get
op getPet(@path petId: int32): {
@body pet: Pet;
} | {
@body error: NotFoundError;
};
@post
op createPet(@body pet: Pet): {
@statusCode statusCode: 201;
@body newPet: Pet;
};
@put
op updatePet(@path petId: int32, @body pet: Pet): {
@body updatedPet: Pet;
} | {
@body error: NotFoundError;
};
@delete
op deletePet(@path petId: int32): {
@statusCode statusCode: 204;
} | {
@body error: NotFoundError;
};
}
@error
model NotFoundError {
code: "NOT_FOUND";
message: string;
}
```
In this example:
- The `deletePet` operation is defined using the `@delete` decorator.
- It takes a `petId` as a path parameter and returns a status code of 204 if the deletion is successful or a `NotFoundError` if the pet is not found.
## Handling Different Types of Responses
In a real-world API, different operations might return different types of responses. Let's see how we can handle various response scenarios in TypeSpec.
### Example: Handling Validation Errors
Let's define a `ValidationError` model and update our `createPet` operation to handle validation errors.
```tsp tryit="{"emit": ["@typespec/openapi3"]}"
import "@typespec/http";
using TypeSpec.Http;
@service({
title: "Pet Store",
})
@server("https://example.com", "Single server endpoint")
namespace PetStore;
model Pet {
id: int32;
@minLength(1)
name: string;
@minValue(0)
@maxValue(100)
age: int32;
kind: petType;
}
enum petType {
dog: "dog",
cat: "cat",
fish: "fish",
bird: "bird",
reptile: "reptile",
}
@route("/pets")
namespace Pets {
@get
op listPets(): {
@body pets: Pet[];
};
@get
op getPet(@path petId: int32): {
@body pet: Pet;
} | {
@body error: NotFoundError;
};
@post
op createPet(@body pet: Pet): {
@statusCode statusCode: 201;
@body newPet: Pet;
} | {
@statusCode statusCode: 400;
@body error: ValidationError;
};
@put
op updatePet(@path petId: int32, @body pet: Pet): {
@body updatedPet: Pet;
} | {
@body error: NotFoundError;
};
@delete
op deletePet(@path petId: int32): {
@statusCode statusCode: 204;
} | {
@body error: NotFoundError;
};
}
@error
model NotFoundError {
code: "NOT_FOUND";
message: string;
}
@error
model ValidationError {
code: "VALIDATION_ERROR";
message: string;
details: string[];
}
```
In this example:
- The `ValidationError` model is defined to represent validation errors.
- The `createPet` operation is updated to handle validation errors by returning a status code of 400 and a `ValidationError` object.
## Conclusion
In this section, we expanded our REST API by defining more HTTP operations, including `POST`, `PUT`, and `DELETE` operations. We also demonstrated how to handle different types of responses, such as validation errors and not found errors.
In the next section, we'll dive deeper into handling errors in your REST API, including defining custom response models for error handling.

Просмотреть файл

@ -0,0 +1,338 @@
---
title: Handling Errors
---
# Handling Errors in Your REST API
## Introduction
In this section, we'll focus on handling errors in your REST API. We've already introduced defining error models in the previous sections, and now we'll expand on that topic. We'll define additional error models, create custom response models for error handling, and demonstrate how to use union types for different response scenarios.
## Defining Error Models
Error models can be used to represent different types of errors that your API might return. Let's start by defining some common error models.
### Example: Defining Common Error Models
We've already defined models to represent validation errors and not-found errors. We'll now add a model for internal server errors:
```tsp tryit="{"emit": ["@typespec/openapi3"]}"
import "@typespec/http";
using TypeSpec.Http;
@service({
title: "Pet Store",
})
@server("https://example.com", "Single server endpoint")
namespace PetStore;
model Pet {
id: int32;
@minLength(1)
name: string;
@minValue(0)
@maxValue(100)
age: int32;
kind: petType;
}
enum petType {
dog: "dog",
cat: "cat",
fish: "fish",
bird: "bird",
reptile: "reptile",
}
@route("/pets")
namespace Pets {
@get
op listPets(): {
@body pets: Pet[];
};
@get
op getPet(@path petId: int32): {
@body pet: Pet;
} | {
@body error: NotFoundError;
};
@post
op createPet(@body pet: Pet): {
@statusCode statusCode: 201;
@body newPet: Pet;
} | {
@statusCode statusCode: 400;
@body error: ValidationError;
};
@put
op updatePet(@path petId: int32, @body pet: Pet): {
@body updatedPet: Pet;
} | {
@body error: NotFoundError;
};
@delete
op deletePet(@path petId: int32): {
@statusCode statusCode: 204;
} | {
@body error: NotFoundError;
};
}
@error
model NotFoundError {
code: "NOT_FOUND";
message: string;
}
@error
model ValidationError {
code: "VALIDATION_ERROR";
message: string;
details: string[];
}
@error
model InternalServerError {
code: "INTERNAL_SERVER_ERROR";
message: string;
}
```
In this example:
- The `ValidationError`, `NotFoundError`, and `InternalServerError` models are defined to represent different types of errors.
- The `@error` decorator is used to indicate that these models represent error responses.
## Custom Response Models for Error Handling
Sometimes, you may need to create custom response models to handle specific error scenarios. Let's define a custom response model for the `InternalServerError` we just created.
### Example: Defining a Custom Response Model
```tsp tryit="{"emit": ["@typespec/openapi3"]}"
import "@typespec/http";
using TypeSpec.Http;
@service({
title: "Pet Store",
})
@server("https://example.com", "Single server endpoint")
namespace PetStore;
model Pet {
id: int32;
@minLength(1)
name: string;
@minValue(0)
@maxValue(100)
age: int32;
kind: petType;
}
enum petType {
dog: "dog",
cat: "cat",
fish: "fish",
bird: "bird",
reptile: "reptile",
}
@route("/pets")
namespace Pets {
@get
op listPets(): {
@body pets: Pet[];
};
@get
op getPet(@path petId: int32): {
@body pet: Pet;
} | {
@body error: NotFoundError;
};
@post
op createPet(@body pet: Pet): {
@statusCode statusCode: 201;
@body newPet: Pet;
} | {
@statusCode statusCode: 400;
@body error: ValidationError;
};
@put
op updatePet(@path petId: int32, @body pet: Pet): {
@body updatedPet: Pet;
} | {
@body error: NotFoundError;
} | InternalServerErrorResponse;
@delete
op deletePet(@path petId: int32): {
@statusCode statusCode: 204;
} | {
@body error: NotFoundError;
};
}
@error
model NotFoundError {
code: "NOT_FOUND";
message: string;
}
@error
model ValidationError {
code: "VALIDATION_ERROR";
message: string;
details: string[];
}
@error
model InternalServerError {
code: "INTERNAL_SERVER_ERROR";
message: string;
}
model InternalServerErrorResponse {
@statusCode statusCode: 500;
@body error: InternalServerError;
}
```
In this example:
- The `InternalServerErrorResponse` model is defined to represent a custom response for a 500 Internal Server Error.
- The `updatePet` operation is updated to respond with with our custom `InternalServerErrorResponse` in case of an internal server error.
## Union Expressions for Different Response Scenarios
Union expressions are a type of [union](../../language-basics/unions.md) that allows you to define operations that can return different responses based on various scenarios.
We've already seen some examples of this, let's expand on how we can use union types to handle different response scenarios in our operations.
### Example: Using Union Expressions for Responses
```tsp tryit="{"emit": ["@typespec/openapi3"]}"
import "@typespec/http";
using TypeSpec.Http;
@service({
title: "Pet Store",
})
@server("https://example.com", "Single server endpoint")
namespace PetStore;
model Pet {
id: int32;
@minLength(1)
name: string;
@minValue(0)
@maxValue(100)
age: int32;
kind: petType;
}
enum petType {
dog: "dog",
cat: "cat",
fish: "fish",
bird: "bird",
reptile: "reptile",
}
@route("/pets")
namespace Pets {
@get
op listPets(): {
@body pets: Pet[];
};
@get
op getPet(@path petId: int32): {
@body pet: Pet;
} | {
@body error: NotFoundError;
};
@post
op createPet(@body pet: Pet): {
@statusCode statusCode: 201;
@body newPet: Pet;
} | {
@statusCode statusCode: 400;
@body error: ValidationError;
};
@put
op updatePet(@path petId: int32, @body pet: Pet):
| {
@body updatedPet: Pet;
}
| {
@body error: NotFoundError;
}
| {
@statusCode statusCode: 400;
@body error: ValidationError;
}
| InternalServerErrorResponse;
@delete
op deletePet(@path petId: int32): {
@statusCode statusCode: 204;
} | {
@body error: NotFoundError;
};
}
@error
model NotFoundError {
code: "NOT_FOUND";
message: string;
}
@error
model ValidationError {
code: "VALIDATION_ERROR";
message: string;
details: string[];
}
@error
model InternalServerError {
code: "INTERNAL_SERVER_ERROR";
message: string;
}
model InternalServerErrorResponse {
@statusCode statusCode: 500;
@body error: InternalServerError;
}
```
In this example:
- The `updatePet` operation is updated to handle multiple response scenarios using union expressions.
- It can return an updated `Pet` object, a `NotFoundError`, a `ValidationError`, or an `InternalServerErrorResponse` custom response model.
## Conclusion
In this section, we focused on defining error handling in your REST API. We expanded on the topic of defining error models, created custom response models for error handling, and demonstrated how to use union expressions for different response scenarios.
In the next section, we'll dive into reusing common parameters in your REST API.

Просмотреть файл

@ -0,0 +1,195 @@
---
title: Common Parameters
---
# Reusing Common Parameters
## Introduction
In this section, we'll focus on reusing common parameters in your REST API. Common parameters are parameters that are used across multiple operations. By defining these parameters once and reusing them, we can make our API more consistent, easier to read, and easier to maintain.
## Creating a Common Parameters Model
Let's start by defining a model for common parameters. This model will include parameters that will be used across all pet store operations.
### Example: Defining Common Parameters
For the sake of demonstration, we're going to require each API call in our pet store service to include a request ID, a locale, and a client version. Let's define a model for these common parameters, which we'll label `requestID`, `locale`, and `clientVersion`:
```typespec
import "@typespec/http";
using TypeSpec.Http;
@service({
title: "Pet Store",
})
@server("https://example.com", "Single server endpoint")
namespace PetStore;
model Pet {
id: int32;
@minLength(1)
name: string;
@minValue(0)
@maxValue(100)
age: int32;
kind: petType;
}
enum petType {
dog: "dog",
cat: "cat",
fish: "fish",
bird: "bird",
reptile: "reptile",
}
model CommonParameters {
@header
requestID: string;
@query
locale?: string;
@header
clientVersion?: string;
}
```
In this example:
- The `@header` decorator is used to indicate that `requestID` and `clientVersion` are header parameters.
- The `@query` decorator is used to indicate that `locale` is a query parameter.
## Reusing Common Parameters Across Multiple Operations
Now that we have defined our common parameters model, let's reuse it across multiple operations in our API.
### Example: Reusing Common Parameters in Operations
```tsp tryit="{"emit": ["@typespec/openapi3"]}"
import "@typespec/http";
using TypeSpec.Http;
@service({
title: "Pet Store",
})
@server("https://example.com", "Single server endpoint")
namespace PetStore;
model Pet {
id: int32;
@minLength(1)
name: string;
@minValue(0)
@maxValue(100)
age: int32;
kind: petType;
}
enum petType {
dog: "dog",
cat: "cat",
fish: "fish",
bird: "bird",
reptile: "reptile",
}
model CommonParameters {
@header
requestID: string;
@query
locale?: string;
@header
clientVersion?: string;
}
@route("/pets")
namespace Pets {
@get
op listPets(...CommonParameters): {
@body pets: Pet[];
};
@get
op getPet(@path petId: int32, ...CommonParameters): {
@body pet: Pet;
} | {
@body error: NotFoundError;
};
@post
op createPet(@body pet: Pet, ...CommonParameters): {
@statusCode statusCode: 201;
@body newPet: Pet;
} | {
@statusCode statusCode: 400;
@body error: ValidationError;
};
@put
op updatePet(@path petId: int32, @body pet: Pet, ...CommonParameters):
| {
@body updatedPet: Pet;
}
| {
@body error: NotFoundError;
}
| {
@statusCode statusCode: 400;
@body error: ValidationError;
}
| InternalServerErrorResponse;
@delete
op deletePet(@path petId: int32, ...CommonParameters): {
@statusCode statusCode: 204;
} | {
@body error: NotFoundError;
};
}
@error
model NotFoundError {
code: "NOT_FOUND";
message: string;
}
@error
model ValidationError {
code: "VALIDATION_ERROR";
message: string;
details: string[];
}
@error
model InternalServerError {
code: "INTERNAL_SERVER_ERROR";
message: string;
}
model InternalServerErrorResponse {
@statusCode statusCode: 500;
@body error: InternalServerError;
}
```
In this example:
- The `CommonParameters` model is reused across multiple operations using the [spread operator](../../language-basics/models#spread) `(...)`, which tells the TypeSpec compiler to expand the model definition inline.
- This approach ensures that the common parameters are consistently applied to all relevant operations, making the API more maintainable and reducing redundancy.
## Conclusion
In this section, we focused on reusing common parameters in your REST API using TypeSpec. By defining a common parameters model and reusing it across multiple operations, we can make our API more consistent, easier to read, and easier to maintain.
In the next section, we'll dive into adding authentication to your REST API.

Просмотреть файл

@ -0,0 +1,142 @@
---
title: Authentication
---
# Adding Authentication to Your REST API
## Introduction
In this section, we'll focus on adding authentication to your REST API. We'll introduce the `@useAuth` decorator, show how to enforce [authentication](../../libraries/http/authentication) on specific operations, and provide an example using Bearer authentication.
## Introduction to the `@useAuth` Decorator
The [@useAuth](../../libraries/http/reference/decorators#@TypeSpec.Http.useAuth) decorator is used to enforce authentication on specific operations in your REST API. This decorator allows you to specify the authentication mechanism that should be used for the operation. The TypeSpec HTTP library provides support for several authentication models, including `BearerAuth` for Bearer authentication.
### Example: Enforcing Authentication on Specific Operations
Let's update our existing operations to enforce authentication using the `@useAuth` decorator. We'll add authentication to the operations that modify pet data, such as creating, updating, and deleting pets.
```tsp tryit="{"emit": ["@typespec/openapi3"]}"
import "@typespec/http";
using TypeSpec.Http;
@service({
title: "Pet Store",
})
@server("https://example.com", "Single server endpoint")
namespace PetStore;
model Pet {
id: int32;
@minLength(1)
name: string;
@minValue(0)
@maxValue(100)
age: int32;
kind: petType;
}
enum petType {
dog: "dog",
cat: "cat",
fish: "fish",
bird: "bird",
reptile: "reptile",
}
model CommonParameters {
@header
requestID: string;
@query
locale?: string;
@header
clientVersion?: string;
}
@route("/pets")
namespace Pets {
@get
op listPets(...CommonParameters): {
@body pets: Pet[];
};
@get
op getPet(@path petId: int32, ...CommonParameters): {
@body pet: Pet;
} | {
@body error: NotFoundError;
};
@post
@useAuth(BearerAuth)
op createPet(@body pet: Pet, ...CommonParameters): {
@statusCode statusCode: 201;
@body newPet: Pet;
} | {
@statusCode statusCode: 400;
@body error: ValidationError;
};
@put
@useAuth(BearerAuth)
op updatePet(@path petId: int32, @body pet: Pet, ...CommonParameters):
| {
@body updatedPet: Pet;
}
| {
@body error: NotFoundError;
}
| {
@statusCode statusCode: 400;
@body error: ValidationError;
}
| InternalServerErrorResponse;
@delete
@useAuth(BearerAuth)
op deletePet(@path petId: int32, ...CommonParameters): {
@statusCode statusCode: 204;
} | {
@body error: NotFoundError;
};
}
@error
model NotFoundError {
code: "NOT_FOUND";
message: string;
}
@error
model ValidationError {
code: "VALIDATION_ERROR";
message: string;
details: string[];
}
@error
model InternalServerError {
code: "INTERNAL_SERVER_ERROR";
message: string;
}
model InternalServerErrorResponse {
@statusCode statusCode: 500;
@body error: InternalServerError;
}
```
In this example:
- The `@useAuth(BearerAuth)` decorator is applied to the `createPet`, `updatePet`, and `deletePet` operations to enforce authentication using the Bearer authentication mechanism.
- Bearer authentication uses tokens for access control. The server generates a token upon login, and the client includes it in the Authorization header for protected resource requests.
## Conclusion
In this section, we focused on adding authentication to your REST API using TypeSpec. By using the `@useAuth` decorator, we can enforce authentication on specific operations, ensuring that only authorized clients can perform certain actions.

Просмотреть файл

@ -0,0 +1,330 @@
---
title: Versioning
---
# Versioning
## Introduction
In this section, we'll focus on implementing versioning in your REST API. Versioning allows you to manage changes to your API over time without breaking existing clients. We'll introduce the `@versioned` decorator, show how to define versions with enums, and demonstrate how to use the `@added` decorator to specify version-specific models and operations.
## Adding the Versioning Library
Before we can use the versioning decorators, we need to add the `@typespec/versioning` library to our project. This involves updating the `package.json` file and installing the necessary dependencies.
### Step 1: Update `package.json`
Add the `@typespec/versioning` library to your `package.json` file:
```json
{
"name": "tsp_pet_store",
"version": "0.1.0",
"type": "module",
"dependencies": {
"@typespec/compiler": "latest",
"@typespec/http": "latest",
"@typespec/openapi3": "latest",
"@typespec/versioning": "latest"
},
"private": true
}
```
### Step 2: Install Dependencies
Run the following command to install the new dependencies:
```sh
tsp install
```
## Introduction to the `@versioned` Decorator
The [`@versioned`](../../libraries/versioning/reference/decorators#@TypeSpec.Versioning.versioned) decorator is used to define different versions of your API. This decorator allows you to specify the versions that your API supports and manage changes across these versions.
### Example: Defining API Versions
Let's define two versions of our API, `v1` and `v2`:
```typespec
import "@typespec/http";
import "@typespec/versioning";
using TypeSpec.Http;
using TypeSpec.Versioning;
@service({
title: "Pet Store",
})
@server("https://example.com", "Single server endpoint")
@versioned(Versions)
namespace PetStore;
enum Versions {
v1: "1.0",
v2: "2.0",
}
```
In this example:
- We're importing and using a new module, `@typespec/versioning`, which provides versioning support.
- The `@versioned` decorator is used to define the versions supported by the API, defined in the `Versions` enum.
- The `Versions` enum specifies two versions: `v1` (1.0) and `v2` (2.0).
## Using the `@added` Decorator
The [`@added`](../../libraries/versioning/reference/decorators#@TypeSpec.Versioning.added) decorator is used to indicate that a model or operation was added in a specific version of the API. This allows you to manage changes and additions to your API over time.
### Example: Adding a New Model in a Specific Version
Let's add a `Toy` model that is only available in version 2 of the API:
```tsp tryit="{"emit": ["@typespec/openapi3"]}"
import "@typespec/http";
import "@typespec/versioning";
using TypeSpec.Http;
using TypeSpec.Versioning;
@service({
title: "Pet Store",
})
@server("https://example.com", "Single server endpoint")
@versioned(Versions)
namespace PetStore;
enum Versions {
v1: "1.0",
v2: "2.0",
}
model Pet {
id: int32;
@minLength(1)
name: string;
@minValue(0)
@maxValue(100)
age: int32;
kind: petType;
}
enum petType {
dog: "dog",
cat: "cat",
fish: "fish",
bird: "bird",
reptile: "reptile",
}
@added(Versions.v2)
model Toy {
id: int32;
name: string;
}
```
In this example:
- The `Toy` model is defined with the `@added(Versions.v2)` decorator to indicate that it was added in version 2 of the API.
## Version-Specific Operations
Let's define version-specific operations to manage toys for pets. These operations will only be available in version 2 of the API.
### Example: Adding Version-Specific Operations
```tsp tryit="{"emit": ["@typespec/openapi3"]}"
import "@typespec/http";
import "@typespec/versioning";
using TypeSpec.Http;
using TypeSpec.Versioning;
@service({
title: "Pet Store",
})
@server("https://example.com", "Single server endpoint")
@versioned(Versions)
namespace PetStore;
enum Versions {
v1: "1.0",
v2: "2.0",
}
model Pet {
id: int32;
@minLength(1)
name: string;
@minValue(0)
@maxValue(100)
age: int32;
kind: petType;
}
enum petType {
dog: "dog",
cat: "cat",
fish: "fish",
bird: "bird",
reptile: "reptile",
}
@added(Versions.v2)
model Toy {
id: int32;
name: string;
}
model CommonParameters {
@header
requestID: string;
@query
locale?: string;
@header
clientVersion?: string;
}
@route("/pets")
namespace Pets {
@get
op listPets(...CommonParameters): {
@body pets: Pet[];
};
@get
op getPet(@path petId: int32, ...CommonParameters): {
@body pet: Pet;
} | {
@body error: NotFoundError;
};
@post
@useAuth(BearerAuth)
op createPet(@body pet: Pet, ...CommonParameters): {
@statusCode statusCode: 201;
@body newPet: Pet;
} | {
@statusCode statusCode: 400;
@body error: ValidationError;
};
@put
@useAuth(BearerAuth)
op updatePet(@path petId: int32, @body pet: Pet, ...CommonParameters):
| {
@body updatedPet: Pet;
}
| {
@body error: NotFoundError;
}
| {
@statusCode statusCode: 400;
@body error: ValidationError;
}
| InternalServerErrorResponse;
@delete
@useAuth(BearerAuth)
op deletePet(@path petId: int32, ...CommonParameters): {
@statusCode statusCode: 204;
} | {
@body error: NotFoundError;
};
@route("{petId}/toys")
namespace Toys {
@added(Versions.v2)
@get
op listToys(@path petId: int32, ...CommonParameters): {
@body toys: Toy[];
};
@added(Versions.v2)
@post
@useAuth(BearerAuth)
op createToy(@path petId: int32, @body toy: Toy, ...CommonParameters): {
@statusCode statusCode: 201;
@body newToy: Toy;
};
@added(Versions.v2)
@put
@useAuth(BearerAuth)
op updateToy(@path petId: int32, @path toyId: int32, @body toy: Toy, ...CommonParameters): {
@body updatedToy: Toy;
};
@added(Versions.v2)
@delete
@useAuth(BearerAuth)
op deleteToy(@path petId: int32, @path toyId: int32, ...CommonParameters): {
@statusCode statusCode: 204;
};
}
}
@error
model NotFoundError {
code: "NOT_FOUND";
message: string;
}
@error
model ValidationError {
code: "VALIDATION_ERROR";
message: string;
details: string[];
}
@error
model InternalServerError {
code: "INTERNAL_SERVER_ERROR";
message: string;
}
model InternalServerErrorResponse {
@statusCode statusCode: 500;
@body error: InternalServerError;
}
```
In this example:
- The `Toys` namespace is defined under the `Pets` namespace.
- The `@added(Versions.v2)` decorator is applied to the operations within the `Toys` namespace to indicate that they were added in version 2 of the API.
- The `Toys` namespace includes operations to list, create, update, and delete toys for a specific pet. These operations are only available in version 2 of the API.
### Generating OpenAPI Specifications for Different Versions
Once we start adding versions, the TypeSpec compiler will generate individual OpenAPI specifications for each version. In our case, it will generate two OpenAPI specs, one for each version of our pet store service API. Our file structure will now look like this:
```
main.tsp
tspconfig.yaml
package.json
node_modules/
tsp-output/
@typespec/
┗ openapi3/
┃ ┣ openapi.1.0.yaml
┃ ┗ openapi.2.0.yaml
```
The 2.0 version of the OpenAPI spec will include the Toy model and any other additions specified for version 2 of our service, while the 1.0 version will not include these additions.
Generating separate specs for each version ensures backward compatibility, provides clear documentation for developers to understand differences between versions, and simplifies maintenance by allowing independent updates to each version's specifications.
## Conclusion
In this section, we focused on implementing versioning in your REST API. By using the `@versioned` and `@added` decorators, we can manage changes to our API over time without breaking existing clients.

Просмотреть файл

@ -0,0 +1,287 @@
---
title: Custom Response Models
---
# Custom Response Models
## Introduction
In this section, we'll focus on creating custom response models. We'll define custom response models, extend base response models, and demonstrate how to use them in your API operations. We'll also incorporate existing response models from the TypeSpec HTTP library.
## Introduction to Custom Response Models
Custom response models allow you to define structured responses for your API operations. They help ensure consistency and clarity in your API responses. TypeSpec defines response models for common HTTP responses in the [HTTP library](https://typespec.io/docs/libraries/http/reference), which we can incorporate into our custom response models.
### Benefits of Using Custom Response Models
- **Reducing Duplication**: By defining common response structures once, you can reuse them across multiple operations.
- **Improving Readability**: Custom response models make your API definitions clearer and easier to understand.
- **Minimizing Errors**: Consistent response models help reduce the likelihood of errors in your API responses.
## Defining Custom Response Models
Let's start by defining some basic custom response models. We'll incorporate existing response models from the TypeSpec HTTP library.
### Example: Defining Basic Custom Response Models
```typespec
import "@typespec/http";
import "@typespec/versioning";
using TypeSpec.Http;
using TypeSpec.Versioning;
model PetListResponse {
...OkResponse;
...Body<Pet[]>;
}
model PetResponse {
...OkResponse;
...Body<Pet>;
}
model PetCreatedResponse {
...CreatedResponse;
...Body<Pet>;
}
model PetErrorResponse {
...BadRequestResponse;
...Body<ValidationError>;
}
```
In this example:
- `PetListResponse` extends `OkResponse` and includes a body with an array of `Pet` objects.
- `PetResponse` extends `OkResponse` and includes a body with a single `Pet` object.
- `PetCreatedResponse` extends `CreatedResponse` and includes a body with a newly created `Pet` object.
- `PetErrorResponse` extends `BadRequestResponse` and includes a body with a `ValidationError` object.
## Extending Base Response Models
We can extend base response models to create more specific responses for different scenarios.
### Example: Extending Base Response Models
```typespec
model PetNotFoundResponse {
...NotFoundResponse;
...Body<NotFoundError>;
}
model PetUnauthorizedResponse {
...UnauthorizedResponse;
...Body<APIError>;
}
model PetSuccessResponse {
...OkResponse;
...Body<string>;
}
```
In this example:
- `PetNotFoundResponse` extends `NotFoundResponse` and includes a body with a `NotFoundError` object.
- `PetUnauthorizedResponse` extends `UnauthorizedResponse` and includes a body with an `APIError` object.
- `PetSuccessResponse` extends `OkResponse` and includes a body with a success message.
## Using Custom Response Models in Operations
Now that we have defined our custom response models, let's use them in our API operations.
### Example: Applying Custom Response Models to Operations
```tsp tryit="{"emit": ["@typespec/openapi3"]}"
import "@typespec/http";
import "@typespec/versioning";
using TypeSpec.Http;
using TypeSpec.Versioning;
@service({
title: "Pet Store",
})
@server("https://example.com", "Single server endpoint")
@versioned(Versions)
namespace PetStore;
enum Versions {
v1: "1.0",
v2: "2.0",
}
model Pet {
id: int32;
@minLength(1)
name: string;
@minValue(0)
@maxValue(100)
age: int32;
kind: petType;
}
enum petType {
dog: "dog",
cat: "cat",
fish: "fish",
bird: "bird",
reptile: "reptile",
}
@added(Versions.v2)
model Toy {
id: int32;
name: string;
}
model CommonParameters {
@header
requestID: string;
@query
locale?: string;
@header
clientVersion?: string;
}
model PetListResponse {
...OkResponse;
...Body<Pet[]>;
}
model PetResponse {
...OkResponse;
...Body<Pet>;
}
model PetCreatedResponse {
...CreatedResponse;
...Body<Pet>;
}
model PetErrorResponse {
...BadRequestResponse;
...Body<ValidationError>;
}
model PetNotFoundResponse {
...NotFoundResponse;
...Body<NotFoundError>;
}
model PetUnauthorizedResponse {
...UnauthorizedResponse;
...Body<ValidationError>;
}
model PetSuccessResponse {
...OkResponse;
...Body<string>;
}
@route("/pets")
namespace Pets {
@get
op listPets(...CommonParameters): PetListResponse | BadRequestResponse;
@get
op getPet(
@path petId: int32,
@header ifMatch?: string,
): PetResponse | PetNotFoundResponse | BadRequestResponse;
@useAuth(BearerAuth)
@post
op createPet(@body pet: Pet): PetCreatedResponse | PetErrorResponse;
@useAuth(BearerAuth)
@put
op updatePet(@path petId: int32, @body pet: Pet):
| PetResponse
| PetNotFoundResponse
| PetUnauthorizedResponse
| InternalServerErrorResponse;
@useAuth(BearerAuth)
@delete
op deletePet(
@path petId: int32,
): PetNotFoundResponse | PetSuccessResponse | PetUnauthorizedResponse;
@route("{petId}/toys")
namespace Toys {
@added(Versions.v2)
@get
op listToys(@path petId: int32, ...CommonParameters): {
@body toys: Toy[];
};
@added(Versions.v2)
@post
@useAuth(BearerAuth)
op createToy(@path petId: int32, @body toy: Toy, ...CommonParameters): {
@statusCode statusCode: 201;
@body newToy: Toy;
};
@added(Versions.v2)
@put
@useAuth(BearerAuth)
op updateToy(@path petId: int32, @path toyId: int32, @body toy: Toy, ...CommonParameters): {
@body updatedToy: Toy;
};
@added(Versions.v2)
@delete
@useAuth(BearerAuth)
op deleteToy(@path petId: int32, @path toyId: int32, ...CommonParameters): {
@statusCode statusCode: 204;
};
}
}
@error
model NotFoundError {
code: "NOT_FOUND";
message: string;
}
@error
model ValidationError {
code: "VALIDATION_ERROR";
message: string;
details: string[];
}
@error
model InternalServerError {
code: "INTERNAL_SERVER_ERROR";
message: string;
}
model InternalServerErrorResponse {
@statusCode statusCode: 500;
@body error: InternalServerError;
}
```
In this example:
- The `listPets` operation uses the `PetListResponse` custom response model.
- The `getPet` operation uses the `PetResponse`, `PetNotFoundResponse`, and `BadRequestResponse` custom response models.
- The `createPet` operation uses the `PetCreatedResponse` and `PetErrorResponse` custom response models.
- The `updatePet` operation uses the `PetResponse`, `PetNotFoundResponse`, `PetUnauthorizedResponse`, and `InternalServerErrorResponse` custom response models.
- The `deletePet` operation uses the `PetNotFoundResponse`, `PetSuccessResponse`, and `PetUnauthorizedResponse` custom response models.
Note that we could also define custom response models for the `Toys` operations, similar to the `Pets` operations. But for brevity, we're omitting them in this example.
## Conclusion
In this section, we focused on creating custom response models in your REST API. By defining and using custom response models, we can reduce duplication, improve readability, and minimize errors in our API responses. We also incorporated existing response models from the TypeSpec HTTP library to ensure consistency and clarity.

Просмотреть файл

@ -0,0 +1,33 @@
---
title: Conclusion
---
# Conclusion
## Recap of Key Concepts
Throughout this tutorial, we've covered a wide range of topics to help you build a comprehensive REST API using TypeSpec. Here's a brief recap of the main concepts we explored:
- **Setting Up the Environment**: We started by setting up the development environment and installing the necessary tools.
- **Defining Models and Services**: We learned how to define models and services using TypeSpec.
- **Creating and Organizing Namespaces**: We organized our API using namespaces to group related models and operations.
- **Defining HTTP Operations**: We defined various HTTP operations, including GET, POST, PUT, and DELETE.
- **Handling Errors**: We created error models and custom response models to handle different types of errors.
- **Reusing Common Parameters**: We defined common parameters and reused them across multiple operations.
- **Adding Authentication**: We enforced authentication on specific operations using the `@useAuth` decorator.
- **Implementing Versioning**: We implemented versioning in our API using the `@versioned` and `@added` decorators.
- **Creating Custom Response Models**: We created custom response models to reduce duplication and improve readability.
## Further Resources
To continue learning and exploring TypeSpec, here are some additional resources:
- [TypeSpec Documentation](https://typespec.io/docs): The official documentation provides detailed information on TypeSpec features and usage.
- [TypeSpec GitHub Repository](https://github.com/microsoft/typespec): The GitHub repository contains the source code and examples.
- [TypeSpec Discord Server](https://aka.ms/typespec/discord): Join the TypeSpec Discord server to participate in discussions, ask questions, and connect with the community.
## Feedback and Community Engagement
We value your feedback and would love to hear about your experiences with this tutorial. Please feel free to share your thoughts and suggestions.
Join the TypeSpec community on Discord to engage with other developers, ask questions, and contribute to discussions. Your participation helps us improve and grow the TypeSpec ecosystem.

Просмотреть файл

@ -5,5 +5,4 @@ title: Getting Started
# Getting started with TypeSpec
- [Get started with TypeSpec for HTTP](./getting-started-http/getting-started-http-00
.md)
- [Get started with TypeSpec for REST](./getting-started-rest/01-setup-basic-syntax.md)

Просмотреть файл

@ -26,7 +26,7 @@ npm install -g @typespec/compiler
## Install the VS and VSCode extensions
TypeSpec provides extension for the following editors:
TypeSpec provides extensions for the following editors:
- [Visual Studio Code](./editor/vscode.md)
- [Visual Studio](./editor/vs.md)
@ -50,13 +50,27 @@ tsp install
You should now have a basic TypeSpec project setup with a structure looking like this:
```bash
package.json # Package manifest defining your TypeSpec project as a Node package.
tspconfig.yaml # TypeSpec project configuration letting you configure emitters, emitter options, compiler options, etc.
main.tsp # TypeSpec entrypoint
main.tsp
tspconfig.yaml
package.json
node_modules/
tsp-output/
@typespec/
openapi3/
openapi.yaml
```
- **main.tsp**: The entry point for your TypeSpec build. This file typically contains the main definitions for your models, services, and operations.
- **tspconfig.yaml**: Configuration file for the TypeSpec compiler, specifying options and settings for the build process.
- **package.json**: Contains metadata about the project, including dependencies, scripts, and other project-related information.
- **node_modules/**: Directory where npm installs the project's dependencies.
- **tsp-output/**: Directory where the TypeSpec compiler outputs generated files.
- **openapi.yaml**: The generated OpenAPI specification file for your API, detailing the API's endpoints, models, and operations. The output can vary based on the target format specified in the `tspconfig.yaml` file.
## Compile project
```bash
tsp compile .
```
You can also run `tsp compile . --watch` to automatically compile changes on save.

Просмотреть файл

@ -55,12 +55,12 @@ const sidebars: SidebarsConfig = {
items: [
{
type: "category",
label: "TypeSpec for HTTP",
link: { type: "doc", id: "getting-started/getting-started-http/index" },
label: "TypeSpec for REST",
link: { type: "doc", id: "getting-started/getting-started-rest/01-setup-basic-syntax" },
items: [
{
type: "autogenerated",
dirName: `getting-started/getting-started-http`,
dirName: `getting-started/getting-started-rest`,
},
],
},