Writing a Greeter Client in TypeScript
This page presents a step-by-step guide to writing the client-side of our TypeScript Greeter application.
This client creates a proxy to a remote object that implements the Greeter
interface and invokes the greet
operation on this object.
You can find the complete source code for this example in the ice-demos repository.
Compile Slice File with Slice Compiler
The first step when writing a TypeScript application with Ice is to compile the Slice definitions for this application with the Slice to JavaScript compiler (slice2js
).
Here, we compile the Greeter.ice
Slice file created earlier. We recommend including this compilation step directly in your project’s build process, as demonstrated in the TypeScript demo programs.
These demos use a simple npm script that runs both slice2js and the TypeScript compiler together.
The Slice compiler generates two files from Greeter.ice
: a TypeScript declaration file, Greeter.d.ts
, and a JavaScript module, Greeter.js
. The declaration file provides the APIs that our client code will call, so generating it is an essential first step in the development process.
Client Implementation
The structure of our client is going to look like:
import { Ice } from "@zeroc/ice";
import { VisitorCenter } from "./Greeter.js";
import process from "node:process";
...
Before anything else, we need to import a few modules:
Ice
is imported from the@zeroc/ice
package and gives us access to the Ice runtime.VisitorCenter
is imported from the generatedGreeter.js
module, produced by theslice2js
compiler.process
is imported from Node’s built-in process module and provides access to command-line arguments.
Then we get to the interesting part: the client logic. We can break this logic down into four pieces:
1. Create a Communicator
First, we create a Communicator with Ice.initialize
:
await using communicator = Ice.initialize(process.argv);
The communicator is the main entry point into the Ice runtime. Its responsibilities include establishing connections to servers, caching these connections, and managing configuration properties. We also need a communicator to create a proxy (see next step).
Our client, like most Ice applications, creates a single communicator.
It is important to make sure that your communicator is properly destroyed when no longer needed. This ensures that network connections are gracefully closed, and other important clean-up occurs. The easiest way to do this is with an await using
like we do here.
2. Create a Greeter Proxy
Next, we need a way to call on a remote Greeter
object. In Ice, this is done with proxies. Proxies are local constructs that represent remote Ice objects and provide methods to call operations on those objects.
We create a Greeter
proxy by constructing an instance of the GreeterPrx
class generated by the Slice compiler:
const greeter = new VisitorCenter.GreeterPrx(
communicator,
"greeter:tcp -h hello.zeroc.com -p 4061");
The constructor accepts our communicator and a “stringified proxy” with the address of the remote Ice object. Here, our stringified proxy says that the target Ice object is named “greeter” and can be reached via tcp
on hello.zeroc.com
on port 4061
.
The name of the interface (Greeter
) and the identity of the Ice object (greeter
) are independent. The Ice objects hosted in the server could just as easily have identities like santa
, bugsBunny
, etc.
3. Make an Invocation
The third step is to call greet
on the remote Ice object using our proxy and to print the greeting:
const username = ...
const greeting = await greeter.greet(username);
console.log(greeting);
The greet
method does all the heavy lifting for us: the proxy creates a request with the username string, the communicator establishes a connection to hello.zeroc.com:4061
, and the request is sent over it. When a response is received, the proxy will unmarshal its payload and finally return a string (the greeting).
Note that greet
returns a Promise
that we await. This allows the JavaScript event loop to continue running other tasks while the invocation completes.
4. Cleanup
Finally, at the end of our logic, our communicator goes out of scope and it is disposed automatically (because we used await using
), and then our application exits.
Running the Client
After building the client (see the demo’s README for instructions), you can run it with:
node client.js
Ice for JavaScript has limited server side support.
Here, we connect to the Greeter server running on hello.zeroc.com
. This Ice server is implemented in a language with full server-side support (C++, C#, Java, Python, or Swift).