# TypeSpec Contracts

> This guide covers creating OpenAPI contracts using [TypeSpec](https://typespec.io/). It contains the necessary files and configurations to define and generate API specifications.

[TOC]

## Key Features

- **TypeSpec Integration**: Leverages TypeSpec libraries for defining APIs, models, and endpoints
- **Versioning Support**: Includes versioning for APIs using the `@versioned` decorator
- **Mocking and Authorization**: Provides options for mocking endpoints and defining authorization scopes and roles
- **OpenAPI File Generation**: Automates the creation of OpenAPI files from TypeSpec definitions

> 💡 This process should be performed using **Visual Studio Code**, as it has the best tooling for working with TypeSpec.

## Folder Structure

- **TypeSpec Files**: Contains TypeSpec code to define API models, endpoints, and configurations
- **Dependencies**: Includes references to TypeSpec libraries and a custom SAIF platform library

## Usage Instructions

1. Open the `repo/src/ApplicationName1.TypeSpec` folder in Visual Studio Code
2. Define your API using TypeSpec syntax:
   - Import required libraries
   - Define models, endpoints, and configurations
   - Customize mocking, authorization, and versioning as needed
3. Generate OpenAPI File:
   1. Open the folder in the integrated terminal
   2. Install required npm packages:
      ```bash
      vsts-npm-auth -config .npmrc
      npm install
      ```
   3. Compile the TypeSpec code to generate the OpenAPI file:
      ```bash
      npm run build
      ```
   4. The generated OpenAPI file will be located in `/infra/api/openapi`

## Key TypeSpec Concepts Used

### Models

Define the structure of API resources.

📖 [Model Creation](https://typespec.io/docs/getting-started/getting-started-rest/01-setup-basic-syntax/#defining-models)

```typescript
@added(Versions.v1)
@resource("people")
model Person {
    @key
    id: string;
    firstName: string;
    lastName: string;
    age: int32;
}
```

### Mocking

Configure mocking behavior for endpoints using `@extension("x-mocking")`.

```typescript
@extension("x-mocking", "true")
```

### Authorization

This defines the default authentication scopes and roles for all endpoints in the namespace. One declaration covers both identity providers — the generated APIM policies check the correct claim per provider:

```typespec
@useAuth(Scopes<["Client.Read"]> | Roles<["App.Read"]>)
```

- `Scopes<[...]>` checks `Client.*` **scopes** (the `scp` claim) — what calling applications request
- `Roles<[...]>` checks `App.*` **roles** (the `roles`/`user-groups` claims) — what is granted to users or applications

Use names that follow the [permission naming convention](../../reference/authorization.md#permission-naming-conventions) and define them in both providers — see [App Permissions](../security/configuration/app-permissions.md). Never reference the platform-managed delegation scopes (`user_impersonation`, `user-groups`) in `@useAuth`; the platform handles those automatically.

### Endpoints

This will create routes based on your model.

The `@autoRoute` decorator ensures that the operations `list`, `get`, `post`, `put` and `delete` are automatically routed based on their names and the HTTP methods they represent. This means you don't have to manually define the routes for these operations, making the code more concise and easier to maintain.

#### Methods

| Method   | Description                                                 | Route Example         |
| -------- | ----------------------------------------------------------- | --------------------- |
| `list`   | Endpoint to get all records                                 | `GET /people`         |
| `get`    | Endpoint to get by `@key` field                             | `GET /people/{id}`    |
| `post`   | Create a record (body excludes `@key`)                      | `POST /people`        |
| `put`    | Replace or create a record (`@key` in route, body excludes) | `PUT /people/{id}`    |
| `patch`  | Update a record (`@key` in route, body excludes)            | `PATCH /people/{id}`  |
| `delete` | Delete a record (`@key` in route)                           | `DELETE /people/{id}` |

#### Endpoint Mocking

This will overwrite the default mocking specified above:

- `true` = Your request will be routed to the mocking URL
- `false` = Your request will be routed to the app backing your endpoint

#### Overriding Authorization

You can override the default authorization on individual endpoints. The scopes and roles must match those defined in your identity provider configuration (see [Authorization](#authorization) above).

```typespec
@added(Versions.v1)
@resource("people")
model Person {
    @key
    id: string;
    firstName: string;
    lastName: string;
    age: int32;
}

@added(Versions.v1)
@resource("orders")
model Order {
    @key
    id: string;
    customerId: string;
    orderDate: utcDateTime;
    totalAmount: decimal;
}

@autoRoute
@added(Versions.v1)
interface People {
    get is ResourceRead<Person>;
    all is ResourceList<Person>;

    @useAuth(Scopes<["Client.Write"]> | Roles<["App.Write"]>)
    post is ResourceCreate<Person>;

    @useAuth(Scopes<["Client.Write"]> | Roles<["App.Write"]>)
    put is ResourceCreateOrReplace<Person>;

    @useAuth(Scopes<["Client.Delete"]> | Roles<["App.Admin"]>)
    delete is ResourceDelete<Person>;
}

@autoRoute
@added(Versions.v1)
interface Orders {
    @useAuth(Scopes<["Client.Write"]> | Roles<["App.Write"]>)
    post is ResourceCreate<Order>;
}
```
