This example is meant to demonstrate a realistic monorepo folder structure where:
frontend-ui
contains the frontend UI that calls some backend APIs (we use Create-React-App)backend-apis
contains all the backend API routes (we use Express.js here, but could easily be a set of serverless functions)- Some routes have a Temporal Client that calls workflows
temporal-worker
has independently scalable Workers that have Workflows and Activities registeredtemporal-workflows
is a shared source folder containing Workflow and Activity code that is used by both the Client inbackend-apis
and the Worker intemporal-worker
packages/
backend-apis/
server.ts # runs Express server on localhost:4000
frontend-ui # this is a Create-React-App on localhost:3000, proxies api requests to :4000
temporal-worker/
worker.ts # registers Temporal Worker that has Workflows and Activities from /temporal-workflows
temporal-workflows/
lib/ # compiled workflows
src/
workflowA/
activities/
activitiesA.ts
activitiesB.ts
workflow.ts
workflowB/
activities/
activitiesC.ts
activitiesD.ts
workflow.ts
all_workflows.ts
all_activities.ts
node_modules
package.json
Notes on the structure demonstrated:
- Workflows require one file: you can organize Workflow code however you like, but each Worker needs to reference a single file that exports all the Workflows it handles (so you have to handle name conflicts instead of us)
- Activities are top level:
- Inside the Temporal Worker, Activities are registered at the same level Workflows are.
- Since Activities are required, not bundled, Activities don't need to be exported in a single file. Just make sure they are registered with some Workers if you intend them to be executed.
- You can organize activities however you like, but it is important to understand that activities don't "belong" to workflows as far as Temporal is concerned.
We built this with yarn
Workspaces. We expect this structure to work with most monorepo tooling: lerna, pnpm, nx, preconstruct, changesets, and Rush but haven't verified it - we cannot support your build tooling specifics but don't mind receiving feedback in our issues.
- Make sure the Temporal Server is running locally. Follow the Quick install guide to do that.
- Run
yarn
to install dependencies. - Run
yarn start
to compile the project, and concurrently start the worker, frontend, and backend server - Open up the UI
localhost:3000
and click the button:
This calls localhost:3000/api/workflow
...
- which CRA proxies to
localhost:4000/api/workflow
and is handled by the Express.js server inbackend-apis
. - The
/api/workflow
route handler has a Temporal Client which executes WorkflowA and starts WorkflowB.
You can see the logs in the terminal output:
[worker] [WorkflowA(a19adbef-9e67-416a-ada6-234e73081e74)] Hello from WorkflowA
[worker] hello from activityA Temporal
[worker] hello from activityB Temporal
[api-server] A: ActivityA result: A-Temporal!, B: ActivityB result: B-Temporal!
[api-server] GET /api/workflow 200 2081.536 ms - 79
[worker] [WorkflowB(68f37547-5e72-46ea-8fd0-54c3adddee53)] Hello from WorkflowB
[worker] hello from activityC in WorkflowB
[worker] hello from activityD in WorkflowB
Both the temporal-worker
and the backend api-server
have been set up to reload whenever temporal-workflows
are edited, with nodemon
.
The workflow code and logs aren't meant to be meaningful, the important thing is that we show how all 4 packages in this monorepo can realistically work together with Temporal spread across 3 of packages in the monorepo.