Skip to main content

Using clients

We have already been using the function createClient in the tutorial. The function gives us a client that uses ECMAScript promise objects. In combination with the await keyword, this lets you write asynchronous code in a natural and easily readable way:

import { createClient } from "@connectrpc/connect";
import { ElizaService } from "@buf/connectrpc_eliza.bufbuild_es/connectrpc/eliza/v1/eliza_pb";

const client = createClient(ElizaService, transport);

const res = await client.say({
sentence: "I feel happy.",
});
console.log(res.sentence);

For server-streaming RPCs, the corresponding method on the client will return an async iterable stream of response messages that can be used with the for await...of statement:

for await (const res of client.introduce({ name: "Joseph" })) {
console.log(res);
}

Callbacks

If you prefer a callback-based approach, the client returned by the function createCallbackClient should suit you:

import { createCallbackClient } from "@connectrpc/connect";
import { ElizaService } from "@buf/connectrpc_eliza.bufbuild_es/connectrpc/eliza/v1/eliza_pb";

const client = createCallbackClient(ElizaService, transport);

client.say({ sentence: "I feel happy." }, (err, res) => {
if (!err) {
console.log(res.sentence);
}
});

For server-streaming RPCs, the corresponding method on the client takes two callback functions: one that is called every time a response message arrives, and one that is called at the end of the stream.

import {ConnectError} from "@connectrpc/connect";

client.introduce({name: "Joseph"}, (res) => {
console.log(res);
}, (err?: ConnectError) => {
if (err) {
console.error(err);
}
});

The callback client is particularly useful if you want to migrate an existing code base from gRPC-web to Connect clients.

Managing clients and transports

In practice, you will likely want to avoid creating a new transport every time you want to use a client. It really depends on the framework of your choice, but there usually is a simple solution to avoid repetition.

For example, you can easily create a custom hook in React:

// use-client.ts
import { useMemo } from "react";
import { ServiceType } from "@bufbuild/protobuf";
import { createConnectTransport } from "@connectrpc/connect-web";
import { createClient, PromiseClient } from "@connectrpc/connect";

// This transport is going to be used throughout the app
const transport = createConnectTransport({
baseUrl: "https://demo.connectrpc.com",
});

/**
* Get a promise client for the given service.
*/
export function useClient<T extends ServiceType>(service: T): PromiseClient<T> {
// We memoize the client, so that we only create one instance per service.
return useMemo(() => createClient(service, transport), [service]);
}

Usage:

await useClient(ElizaService).say({sentence: "I feel happy."});