TypeSpec Contracts¶
This guide covers creating OpenAPI contracts using TypeSpec. It contains the necessary files and configurations to define and generate API specifications.
Key Features¶
- TypeSpec Integration: Leverages TypeSpec libraries for defining APIs, models, and endpoints
- Versioning Support: Includes versioning for APIs using the
@versioneddecorator - 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¶
- Open the
repo/src/ApplicationName1.TypeSpecfolder in Visual Studio Code - Define your API using TypeSpec syntax:
- Import required libraries
- Define models, endpoints, and configurations
- Customize mocking, authorization, and versioning as needed
- Generate OpenAPI File:
- Open the folder in the integrated terminal
- Install required npm packages:
- Compile the TypeSpec code to generate the OpenAPI file:
- The generated OpenAPI file will be located in
/infra/api/openapi
Key TypeSpec Concepts Used¶
Models¶
Define the structure of API resources.
@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").
Authorization¶
This defines the default authentication scopes and roles for all endpoints in the namespace.
Microsoft Entra ID (Internal/Corporate Users)¶
For internal corporate authentication, scopes are configured in Microsoft Entra ID app registrations.
- Configuration:
/infra/auth/corp/
⚠️ Important: The
user_impersonationscope is a delegated permission from Microsoft Entra ID that allows the application to access the API on behalf of the signed-in user. This scope must be:
- Exposed in your API's app registration under "Expose an API" → "Add a scope" with the name
user_impersonation- Granted to client applications that need to call your API
When a client application requests an access token with this scope, the token will contain the user's identity and permissions, allowing the API to perform actions as that user. This is the standard pattern for Experience APIs that need to act on behalf of users.
Okta (External Users)¶
For external user authentication via Okta, scopes and roles are defined in YAML configuration files:
- Scopes:
/infra/auth/ext/okta-client/scopes.yml - Roles:
/infra/auth/ext/okta-client/user_groups.yml
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 URLfalse= 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 above).
Here is an example of models and their associated endpoints using Okta (external) authentication:
@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<["write"]> | Roles<["write"]>)
post is ResourceCreate<Person>;
@useAuth(Scopes<["write"]> | Roles<["write"]>)
put is ResourceCreateOrReplace<Person>;
@useAuth(Scopes<["delete"]> | Roles<["admin"]>)
delete is ResourceDelete<Person>;
}
@autoRoute
@added(Versions.v1)
interface Orders {
@useAuth(Scopes<["write"]> | Roles<["write"]>)
post is ResourceCreate<Order>;
}