Clients
This page explains client behavior and how to customize clients. For an overview of the setup, please refer to the previous page.
NOTE: JS RLC does not support customization. It will ignore
client.tsp
, and the following scenarios will not affect the JS RLC user experience. In this context, “TypeScript part” refers to the JS Modular Emitter.
Default Behavior
Basic Rules
By default, the namespace with the @service
decorator is generated as a root client. The client name is the namespace name with Client
appended as a suffix.
Sub-namespaces and interfaces under each root client are generated as operation groups with a hierarchical structure.
The sequence of operation groups is determined by the order of namespace declarations, followed by interface declarations.
The root client’s SDK namespace corresponds to the namespace decorated with @service
. If an operation group originates from a sub-namespace, its SDK namespace corresponds to that sub-namespace. If it originates from an interface, its SDK namespace corresponds to the namespace containing the interface.
By default, operation groups can only be initialized by the root client or their parent operation group.
Different languages organize clients and operation groups differently. Refer to the examples below for details.
Single Client
@service(#{ title: "Pet Store" })namespace PetStore;
@route("/feed")op feed(): void;
@route("/op2")op pet(): void;
from pet_store import PetStoreClient
client = PetStoreClient()client.feed()client.pet()
using PetStore;
PetStoreClient client = new PetStoreClient();client.Feed();client.Pet();
import { PetStoreClient } from "@azure/package-name";
const client = new PetStoreClient();client.feed();client.pet();
PetStoreClient client = new PetStoreClientBuilder().buildClient();client.feed();client.pet();
Client with One-Layer Child Operation Groups
PetStore
has two operation groups. The Dogs
operation group comes from a sub-namespace, while Cats
comes from an interface.
@service(#{ title: "Pet Store", version: "v1" })namespace PetStore;
@route("/dogs")interface Dogs { feed(): void; pet(): void;}
@route("/cats")namespace Cats { op feed(): void; op pet(): void;}
from pet_store import PetStoreClient
client = PetStoreClient()client.dogs.feed()client.dogs.pet()client.cats.feed()client.cats.pet()
using PetStore;
PetStoreClient client = new PetStoreClient();client.GetDogsClient().Feed();client.GetDogsClient().Pet();client.GetCatsClient().Feed();client.GetCatsClient().Pet();
import { PetStoreClient } from "@azure/package-name";
const client = new PetStoreClient();client.dogs.feed();client.dogs.pet();client.cats.feed();client.cats.pet();
PetStoreClientBuilder builder = new PetStoreClientBuilder();
DogsClient dogsClient = builder.buildDogsClient();dogsClient.feed();dogsClient.pet();
CatsClient catsClient = builder.buildCatsClient();catsClient.feed();catsClient.pet();
Client with Multi-Layer Child Operation Groups
PetStore
has three operation groups: Billings
(from an interface), Pets
(from a sub-namespace), and Actions
(from an interface). Pets
also contains a nested operation group, Actions
, from an interface.
@service(#{ title: "Pet Store", version: "v1" })namespace PetStore;
@route("/info")op info(): void;
@route("/billings")interface Billings { @route("history") history(): void;}
@route("/pets")namespace Pets { @route("info") op info(): void;
@route("/actions") interface Actions { feed(): void; pet(): void; }}
@route("/actions")interface Actions { open(): void; close(): void;}
from pet_store import PetStoreClient
client = PetStoreClient()client.info()client.billings.history()client.pets.info()client.pets.actions.feed()client.pets.actions.pet()client.actions.open()client.actions.close()
using PetStore;
PetStoreClient client = new PetStoreClient();client.Info();client.GetBillingsClient().History();client.GetPetsClient().Info();client.GetPetsClient().GetPetsActionsClient().Feed();client.GetPetsClient().GetPetsActionsClient().Pet();client.GetActionsClient().Open();client.GetActionsClient().Close();
import { PetStoreClient } from "@azure/package-name";
const client = new PetStoreClient();client.info();client.billings.history();client.pets.info();client.pets.actions.feed();client.pets.actions.pet();client.actions.open();client.actions.close();
PetStoreClientBuilder builder = new PetStoreClientBuilder();
PetStoreClient petStoreClient = builder.buildClient();petStoreClient.info();
BillingsClient billingsClient = builder.buildBillingsClient();billingsClient.history();
PetsClient petsClient = builder.buildPetsClient();petsClient.info();
PetsActionsClient petsActionsClient = builder.buildPetsActionsClient();petsActionsClient.feed();petsActionsClient.pet();
ActionsClient actionsClient = builder.buildActionsClient();actionsClient.open();actionsClient.close();
Customizations
Customizations SHOULD always be made in a file named client.tsp
alongside main.tsp
.
You can use @client
and @operationGroup
to restructure the client hierarchy. However, if any customizations are made, the client hierarchy will only reflect those customizations. The default behavior logic will no longer apply.
If customizations are made, the client’s SDK namespace will follow the namespace decorated with @client
or the namespace containing the interface decorated with @client
. Similarly, the operation group’s SDK namespace follows the same logic for @operationGroup
. You can override this using @clientNamespace
if needed.
The sequence of clients and operation groups is determined by the order of the @client
and @operationGroup
decorators.
For this section, we assume you have a service called PetStore
in the PetStore
namespace, defining two operations: feed
and pet
.
Renaming the Client Name
This can be achieved with the augment decorator: @clientName
from typespec-client-generator-core
.
import "./main.tsp";import "@azure-tools/typespec-client-generator-core";
using Azure.ClientGenerator.Core;
@@clientName(PetStore, "PetStoreGreatClient");
from pet_store import PetStoreGreatClient
client = PetStoreGreatClient()client.feed()client.pet()
using PetStore;
PetStoreGreatClient client = new PetStoreGreatClient();client.Feed();client.Pet();
import { PetStoreGreatClient } from "@azure/package-name";
const client = new PetStoreGreatClient();client.feed();client.pet();
PetStoreGreatClient client = new PetStoreGreatClientBuilder().buildClient();client.feed();client.pet();
Renaming the Client Namespace
This can be achieved with the augment decorator: @clientNamespace
from typespec-client-generator-core
.
import "./main.tsp";import "@azure-tools/typespec-client-generator-core";
using Azure.ClientGenerator.Core;
@@clientNamespace(PetStore, "PetStoreRenamed");
from pet_store_renamed import PetStoreClient
client = PetStoreClient()client.feed()client.pet()
using PetStoreRenamed;
PetStoreClient client = new PetStoreClient();client.Feed();client.Pet();
import { PetStoreClient } from "@azure/package-name";
const client = new PetStoreClient();client.feed();client.pet();
package petstorerenamed;
PetStoreClient client = new PetStoreClientBuilder().buildClient();client.feed();client.pet();
Splitting the Operations into Two Clients
Two clients that separate the operations can be declared using the @client
decorator from typespec-client-generator-core
.
import "./main.tsp";import "@azure-tools/typespec-client-generator-core";
using Azure.ClientGenerator.Core;
namespace PetStoreRenamed; // this namespace will be the namespace of the clients and operation groups defined in this customization file
@client({ name: "FoodClient", service: PetStore,})interface Client1 { feed is PetStore.feed;}
@client({ name: "PetActionClient", service: PetStore,})interface Client2 { pet is PetStore.pet;}
from pet_store_renamed import FoodClient, PetActionClient
client1 = FoodClient()client2 = PetActionClient()
client1.feed()client2.pet()
using PetStoreRenamed;
PetActionClient petActionClient = new PetActionClient();FoodClient foodClient = new FoodClient();
petActionClient.Pet();foodClient.Feed();
import { FoodClient, PetActionClient } from "@azure/package-name";
const client1 = new PetActionClient();const client2 = new FoodClient();client1.pet();client2.feed();
package petstorerenamed;
FoodClient foodClient = new FoodClientBuilder().buildClient();PetActionClient petActionClient = new PetActionClientBuilder().buildClient();
foodClient.feed()petActionClient.pet()
One Client and Two Operation Groups
Two clients that separate the operations can be declared using the @client
decorator and the @operationGroup
decorator from typespec-client-generator-core
:
import "./main.tsp";import "@azure-tools/typespec-client-generator-core";
using Azure.ClientGenerator.Core;
@client({ name: "PetStoreClient", service: PetStore,})namespace PetStoreRenamed; // this namespace will be the namespace of the clients and operation groups defined in this customization file
@operationGroupinterface OpGrp1 { feed is PetStore.feed;}
@operationGroupinterface OpGrp2 { pet is PetStore.pet;}
from pet_store_renamed import PetStoreClient
client = PetStoreClient()
client.op_grp_1.feed()client.op_grp_2.pet()
using PetStoreRenamed;
PetStoreClient client = new PetStoreClient();
client.GetOpGrp1Client().Feed();client.GetOpGrp2Client().Pet();
import { PetStoreClient } from "@azure/package-name";
const client = new PetStoreClient();client.opGrp1.feed();client.opGrp2.pet();
package petstorerenamed;
PetStoreClientBuilder builder = new PetStoreClientBuilder();
OpGrp1Client opGrp1Client = builder.buildOpGrp1Client();opGrp1Client.feed();
OpGrp2Client opGrp2Client = builder.buildOpGrp2Client();opGrp2Client.pet();
Splitting the Operations into Sub-Namespaces
import "./main.tsp";import "@azure-tools/typespec-client-generator-core";
using Azure.ClientGenerator.Core;
namespace NewPetStore;
@client({ name: "FoodClient", service: PetStore,})namespace Food { op feed is PetStore.feed;}
@client({ name: "PetActionClient", service: PetStore,})namespace PetAction { op pet is PetStore.pet;}
from new_pet_store.food import FoodClientfrom new_pet_store.pet_action import PetActionClient
client1 = FoodClient()client2 = PetActionClient()
client1.feed()client2.pet()
// TODO
NOT_SUPPORTED;
import newpetstore.food.FoodClient;import newpetstore.food.FoodClientBuilder;import newpetstore.petaction.PetActionClient;import newpetstore.petaction.PetActionClientBuilder;
FoodClient foodClient = new FoodClientBuilder().buildClient();foodClient.feed();
PetActionClient petActionClient = new PetActionClientBuilder().buildClient();petActionClient.pet();
Splitting the Operations into Two Clients and Having Clients in Different Namespaces
Two clients that separate the operations can be declared using the client
decorator of typespec-client-generator-core
:
import "./main.tsp";import "@azure-tools/typespec-client-generator-core";
using Azure.ClientGenerator.Core;
namespace PetStoreRenamed; // this namespace will be the namespace of the clients and operation groups defined in this customization file
@client({ name: "FoodClient", service: PetStore,})interface Client1 { feed is PetStore.feed;}
@client({ name: "PetActionClient", service: PetStore,})@clientNamespace("PetStoreRenamed.SubNamespace") // use @clientNamespace to specify the namespace of the clientinterface Client2 { pet is PetStore.pet;}
from pet_store_renamed import FoodClientfrom pet_store_renamed.sub_namespace import PetActionClient
client1 = FoodClient()client2 = PetActionClient()
client1.feed()client2.pet()
using PetStoreRenamed;using PetStoreRenamed.SubNamespace;
SubNamespacePetActionClient petActionClient = new SubNamespacePetActionClient();FoodClient foodClient = new FoodClient();
petActionClient.Pet();foodClient.Feed();
NOT_SUPPORTED;
import petstorerenamed.FoodClient;import petstorerenamed.FoodClientBuilder;import petstorerenamed.subnamespace.PetActionClient;import petstorerenamed.subnamespace.PetActionClientBuilder;
FoodClient foodClient = new FoodClientBuilder().buildClient();PetActionClient petActionClient = new PetActionClientBuilder().buildClient();
foodClient.feed();petActionClient.pet();
Adding Client Initialization Parameters
By default, we only generate our clients with initialization parameters for endpoint
, credential
, and apiVersion
, whenever any of these are applicable.
There are cases where spec authors would like their clients to have additional input parameters.
With @clientInitialization
, you can pass in additional parameters you would like your client to have, by passing in parameters
option of a model.
All properties of the model will be appended to the current default list of client initialization parameters.
Additionally, these client parameters will no longer appear on service methods that previously had them as part of the method signature.
The generated code will automatically pass in the inputted value from the client init to the service.
import "./main.tsp";import "@azure-tools/typespec-client-generator-core";
using Azure.ClientGenerator.Core;
namespace Customizations;
model StorageClientOptions { blobName: string;}
@@clientInitialization(Storage, { parameters: StorageClientOptions, });
from storage import StorageClient
client = StorageClient(endpoint="<my-endpoint>", blob_name="myBlobName", ...)
client.upload()client.download()
// TODO: how do you pass in the options modelusing Storage;
StorageClient client = new StorageClient();
client.Upload();client.Download();
// TODO: how do you pass in the options modelimport { StorageClient } from "@azure/package-name";
const client = new StorageClient();
client.upload();client.download();
package storage;
StorageClient client = new StorageClient() .endpoint("<my-endpoint>") .blobName("myBlobName") .buildClient();
client.upload()client.download()
If you want to rename the parameter name that you elevate to the client level, you can use the @paramAlias
decorator.
import "./main.tsp";import "@azure-tools/typespec-client-generator-core";
using Azure.ClientGenerator.Core;
namespace Customizations;
model StorageClientOptions { @paramAlias("blobName") blob: string;}
@@clientInitialization(Storage, { parameters: StorageClientOptions, });
from storage import StorageClient
client = StorageClient(endpoint="<my-endpoint>", blob="myBlobName", ...)
client.upload()client.download()
// TODO: how do you pass in the options modelusing Storage;
StorageClient client = new StorageClient();
client.Upload();client.Download();
// TODO: how do you pass in the options modelimport { StorageClient } from "@azure/package-name";
const client = new StorageClient();
client.upload();client.download();
package storage;
StorageClient client = new StorageClient() .endpoint("<my-endpoint>") .blob("myBlobName") .buildClient();
client.upload()client.download()
Change Operation Group Initialization Way
By default, all the nested operation group could only be initialized by parent client or operation group. There are cases where spec authors would like their operation groups could both be initialized by parent as well as individually.
With @clientInitialization
, you can change the initialization way, by passing in initializedBy
option of InitializedBy.individually | InitializedBy.parent
value.
import "./main.tsp";import "@azure-tools/typespec-client-generator-core";
using Azure.ClientGenerator.Core;
namespace Customizations;
model StorageClientOptions { blobName: string;}
@@clientInitialization(Storage, { initializedBy: InitializedBy.individually | InitializedBy.parent, });
# TODO
// TODO
// TODO
// TODO