Skip to content

Clients

This page documents the default client hierarchy 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 nested namespacess and interfaces of each root client will be generated as operation groups with hierarchy.

The root client’s namespace will follow the namespace decorated with @service. If an operation group comes from a nested namespace, it will follow that namespace. If it comes from a nested interface, it will follow the namespace to which the interface belongs.

Different language’s code generator will have different way to organize clients and operation groups. Please refer the following examples.

Single client

main.tsp
@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()

Client with one-layer child operation groups

@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()

Client with multi-layer child operation group

main.tsp
@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()

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 namespace will follow the namespace decorated with @client or the namespace to which the interface decorated with @client belongs. The operation group’s 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.

client.tsp
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()

Renaming the client namespace

This can be achieved with the augment decorator: @clientNamespace from typespec-client-generator-core.

client.tsp
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()

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()

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:

client.tsp
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
@operationGroup
interface OpGrp1 {
feed is PetStore.feed;
}
@operationGroup
interface OpGrp2 {
pet is PetStore.pet;
}
from pet_store_renamed import PetStoreClient
client = PetStoreClient()
client.op_grp_1.feed()
client.op_grp_2.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:

client.tsp
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 client
interface Client2 {
pet is PetStore.pet;
}
from pet_store_renamed import FoodClient
from pet_store_renamed.sub_namespace import PetActionClient
client1 = FoodClient()
client2 = PetActionClient()
client1.feed()
client2.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.

client.tsp
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()