Clients
This page documents the client behavior as well as how to customize clients. For an overview of the setup, please visit the previous page.
JS RLC is not in the business of customization. it will ignore client.tsp and the follow scenarios will not have impact on the JS RLC user experiences. In this context, TypeScript part means JS Modular Emitter.
Default behavior
By default, each namespace with @service
decorator will be generated as a root client. The name for that client will be the namespace name concatenating Client
as suffix.
Other sub namespaces and interfaces of each root client will be generated as operation groups with hierarchy.
The root client’s SDK namespace will follow the namespace decorated with @service
. If an operation group comes from a sub namespace, its SDK namespace will follow that namespace. If it comes from a interface, its SDK namespace will follow the namespace which the interface belongs to.
Different language’s code generator will have different way to organize clients and operation groups. Please refer the following examples.
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. Operation group Dogs
is from a sub namespace, and Cats
is 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 group
PetStore
has three operation groups. Operation group Billings
is from an interface, Pets
is from a sub namespace, and Actions
is from an interface. Pets
has one 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
Customization SHOULD always be done in a file called client.tsp
along with the main.tsp
.
You can use @client
and @operationGroup
to reconstruct the client hierarchy. However, if any customizations are made, the client hierarchy will only be inferred from those customizations. The logic defined in the default behaviors will no longer take effect.
If any customizations are made, the client’s SDK namespace will follow the namespace decorated with @client
or the namespace which the interface decorated with @client
belongs to. The operation group’s SDK namespace follows the same logic for @operationGroup
. You can use @clientNamespace
to override it if needed.
For this section, we will assume that you have service called PetStore
in the namespace PetStore
, defining the 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 in two clients and have clients in different namespace
Two clients that separates 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. This is where the @clientInitialization
decorator comes in.
With @clientInitialization
, you can pass in additional parameters you would like your client to have, by passing in an input model. All properties of the input 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, 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()