Operations and Responses
Introduction
In this section, we’ll build upon the basics we covered in the previous section. We’ll define CRUD operations (Create, Read, Update, Delete) for our Pet Store API and discuss the benefits of using nested namespaces.
Defining CRUD Operations
Next, we’ll discuss how to define CRUD operations for our API. We’ll cover operations for Creating
, Reading
, Updating
, and Deleting
pets, all within a nested namespace for better organization.
Example: Adding CRUD Operations
Let’s define the CRUD operations for our Pet
model:
In this example:
- The
@route
decorator defines the base path for thePets
namespace. - The
listPets
operation lists all pets. - The
getPet
operation retrieves a specific pet by itspetId
. - The
createPet
operation creates a new pet. - The
updatePet
operation updates an existing pet. - The
deletePet
operation deletes an existing pet.
Benefits of Nested Namespaces
Using nested namespaces in TypeSpec provides several benefits:
- Organization: Grouping related operations under a common namespace makes the API easier to manage and understand.
- Operation IDs: The TypeSpec compiler appends the namespace name to the
operationId
in the OpenAPI spec, making it clear which resource each operation is intended to operate on. - Clarity: It helps in avoiding naming conflicts and provides a clear structure for the API.
Example: Operation ID in OpenAPI Spec
For the listPets
operation defined in the Pets
namespace, the OpenAPI spec will generate an operationId
like Pets_listPets
, making it clear that this operation is related to the Pets
resource.
Example: Route URLs for CRUD Operations
Here’s what the route URLs will look like for the CRUD operations defined in the Pets
namespace:
- List Pets:
GET https://example.com/pets
- Retrieves a list of all pets.
- Get Pet by ID:
GET https://example.com/pets/{petId}
- Retrieves a specific pet by its
petId
.
- Retrieves a specific pet by its
- Create Pet:
POST https://example.com/pets
- Creates a new pet.
- Update Pet by ID:
PUT https://example.com/pets/{petId}
- Updates an existing pet by its
petId
.
- Updates an existing pet by its
- Delete Pet by ID:
DELETE https://example.com/pets/{petId}
- Deletes an existing pet by its
petId
.
- Deletes an existing pet by its
Operation Flowchart
For clarity, here’s a flowchart that depicts the flow of data and operations within the API:
Handling Different Types of Responses
In a real-world API, different operations might return different types of successful responses. Let’s see how we can handle various response scenarios in TypeSpec.
Example: Handling Different Status Codes
Let’s update our pet operations to return different status codes based on the outcome.
In this example:
- The pet operations are updated to handle different status codes, depending on the outcome of the operation reported by the backend service.
Explanation of the |
Operator:
- The
|
operator is used to define multiple possible responses for an operation. Each response block specifies a different status code and response body. - In the
createPet
operation for example, the|
operator allows the operation to return either a 201 status code with anewPet
object or a 202 status code with anacceptedPet
object.
OpenAPI Spec Mapping
Here is how the TypeSpec operation definitions map to the OpenAPI specification:
TypeSpec Definition:
@route("/pets") namespace Pets {
@get op listPets(): { @statusCode statusCode: 200; @body pets: Pet[]; };
@get op getPet(@path petId: int32): { @statusCode statusCode: 200; @body pet: Pet; } | { @statusCode statusCode: 404; };
@post op createPet(@body pet: Pet): { @statusCode statusCode: 201; @body newPet: Pet; } | { @statusCode statusCode: 202; @body acceptedPet: Pet; };
@put op updatePet(@path petId: int32, @body pet: Pet):{ @statusCode statusCode: 200; @body updatedPet: Pet; } | { @statusCode statusCode: 404; } | { @statusCode statusCode: 500; };
@delete op deletePet(@path petId: int32): { @statusCode statusCode: 204; @body NoContentResponse; } | { @statusCode statusCode: 404; }; }
OpenAPI Spec:
paths:
/pets:
get:
operationId: Pets_listPets
parameters: []
responses:
'200':
description: The request has succeeded.
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'
post:
operationId: Pets_createPet
parameters: []
responses:
'201':
description: The request has succeeded and a new resource has been created as a result.
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
'202':
description: The request has been accepted for processing, but processing has not yet completed.
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
/pets/{petId}:
get:
operationId: Pets_getPet
parameters:
- name: petId
in: path
required: true
schema:
type: integer
format: int32
responses:
'200':
description: The request has succeeded.
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
'404':
description: The server cannot find the requested resource.
put:
operationId: Pets_updatePet
parameters:
- name: petId
in: path
required: true
schema:
type: integer
format: int32
responses:
'200':
description: The request has succeeded.
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
'404':
description: The server cannot find the requested resource.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
delete:
operationId: Pets_deletePet
parameters:
- name: petId
in: path
required: true
schema:
type: integer
format: int32
responses:
'204':
description: 'There is no content to send for this request, but the headers may be useful. '
Note: As you can see, TypeSpec is much more compact and easier to read compared to the equivalent OpenAPI specification.
Conclusion
In this section, we demonstrated how to define CRUD operations for your REST API using TypeSpec and discussed the benefits of using nested namespaces. We also covered how to handle different types of successful responses.
In the next section, we’ll dive deeper into handling errors in your REST API, including defining custom response models for error handling.