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:
import "@typespec/http";
using 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",}
// highlight-start@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;  };
  @post  op createPet(@body pet: Pet): {    @statusCode statusCode: 201;    @body newPet: Pet;  };
  @put  op updatePet(@path petId: int32, @body pet: Pet): {    @statusCode statusCode: 200;    @body updatedPet: Pet;  };
  @delete  op deletePet(@path petId: int32): {    @statusCode statusCode: 204;  };}// highlight-endIn this example:
- The @routedecorator defines the base path for thePetsnamespace.
- The listPetsoperation lists all pets.
- The getPetoperation retrieves a specific pet by itspetId.
- The createPetoperation creates a new pet.
- The updatePetoperation updates an existing pet.
- The deletePetoperation 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 operationIdin 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:
[Client] --> [API Gateway] --> [listPets Operation] --> [Database] --> [Response: List of Pets][Client] --> [API Gateway] --> [getPet Operation] --> [Database] --> [Response: Pet Details][Client] --> [API Gateway] --> [createPet Operation] --> [Database] --> [Response: Created Pet][Client] --> [API Gateway] --> [updatePet Operation] --> [Database] --> [Response: Updated Pet][Client] --> [API Gateway] --> [deletePet Operation] --> [Database] --> [Response: Deletion Confirmation]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.
import "@typespec/http";
using 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(): {    @statusCode statusCode: 200;    @body pets: Pet[];  };
  @get  op getPet(@path petId: int32): {    @statusCode statusCode: 200;    @body pet: Pet;    // highlight-start  } | {    @statusCode statusCode: 404;    // highlight-end  };
  @post  op createPet(@body pet: Pet): {    @statusCode statusCode: 201;    @body newPet: Pet;    // highlight-start  } | {    @statusCode statusCode: 202;    @body acceptedPet: Pet;    // highlight-end  };
  @put  op updatePet(@path petId: int32, @body pet: Pet): {    @statusCode statusCode: 200;    @body updatedPet: Pet;    // highlight-start  } | {    @statusCode statusCode: 404;    // highlight-end  };
  @delete  op deletePet(@path petId: int32): {    @statusCode statusCode: 204;  };}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 createPetoperation, for example, the|operator allows the operation to return either a 201 status code with anewPetobject or a 202 status code with anacceptedPetobject.
OpenAPI Spec Mapping
Here is how the TypeSpec operation definitions map to the OpenAPI specification:
| TypeSpec Definition | OpenAPI Spec | 
|  |  | 
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.