Skip to content

Getting started

This tutorial assumes you’ve already set up a monorepo for your project, for example:

.
├── client
│ ├── src
│ │ ├── components
│ │ └── index.ts
│ ├── public
│ └── ...
├── server
│ ├── src
│ │ ├── services
│ │ ├── validators
│ │ └── index.ts
│ ├── ...
└── shared
├── src
│ ├── types
│ ├── utils
│ └── index.ts
└── ...

TSConfig

Tempo utilizes decorators. As such, you’ll need to set experimentalDecorators and emitDecoratorMetadata to true in your tsconfig.json:

{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"esModuleInterop": true,
"strict": true,
"outDir": "dist",
"experimentalDecorators": true,
"emitDecoratorMetadata": true
},
"include": ["server", "client", "shared"]
}

Code Generation

Tempo uses Bebop for generating client and service code. To install it, follow the commands below:

yarn add bebop
yarn add bebop-tools --dev

By default, bebopc produces client and server code in the same output. You’ll need to add the following dependencies to your project:

yarn add @tempojs/client @tempojs/server

If your client and server code lives in the same project, you can install Tempo into it.

{
"include": [
"**/*.bop"
],
"generators": {
"ts": {
"outFile": "./shared/index.ts"
}
}
}

Now, you can create your first service. Create a ‘greeter.bop’ file in your shared project with the following contents:

/**
* `HelloRequest` is a struct representing a request to the Greeter service.
*/
struct HelloRequest {
/**
* The name to be used in the greeting, of type string.
*/
string name;
}
/**
* `HelloResponse` is a struct representing the response from the Greeter service.
*/
struct HelloResponse {
/**
* The greeting message generated by the service, of type string.
*/
string serviceMessage;
}
/**
* `Greeter` is a service that provides a method for generating greeting messages.
*/
service Greeter {
/**
* `sayHello` is a method that takes a `HelloRequest` and returns a `HelloResponse`.
*/
sayHello(HelloRequest): HelloResponse;
}

Now, just run yarn bebopc build and it will generate all the shared code into index.ts.

Implementing a Service

The Bebop compiler produces concrete client implementations and abstract base service for you to implement. For example:

// registers 'GreeterService' with the 'TempoServiceRegistry'
// we use 'BaseGreeterService.serviceName' as this is how
// the service is referenced by generated code
@TempoServiceRegistry.register(BaseGreeterService.serviceName)
export class GreeterService extends BaseGreeterService {
public sayHello(
record: IHelloRequest,
context: ServerContext
): Promise<IHelloResponse> {
return Promise.resolve({ serviceMessage: `Hello ${record.name}` });
}
}

A service that you wish to expose to clients must be registered using the TempoServiceRegistry.register decorator as shown above. The parameter passed to TempoServiceRegistry.register is always the serviceName present on the base class of the service. Failure to register a service that was defined in a schema will result in a runtime error when initializing the server. At this time it is recommended that you reexport all services under a single export:

index.ts
import { GreeterService } from './greeeter'
export {
GreeterService
}