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 bebopyarn 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:
import { GreeterService } from './greeeter'
export { GreeterService}