Skip to main content

From within your app

By "internal" to a Reboot application, we specifically mean within one of the methods that you've implemented for your servicers. Each of those methods takes a context argument that you can use to make calls:

  • ReaderContext - Passed to a reader.
  • WriterContext - Passed to a writer.
  • TransactionContext - Passed to a transaction.
  • WorkflowContext - Passed to a workflow.

The types of these contexts allow Reboot (as well as static type checkers like mypy, Pyright, or tsc) to enforce its safety guarantees throughout the call graph.

Making calls to other durable data types

Within your servicer methods, you can call methods on other durable data types by first getting a reference to it using .ref(), then calling the method while passing along the context from your servicer method:

async def transfer(
self,
context: TransactionContext,
request: Bank.TransferRequest,
) -> None:
from_account = Account.ref(request.from_account_id)
to_account = Account.ref(request.to_account_id)

await from_account.withdraw(context, amount=request.amount)
await to_account.deposit(context, amount=request.amount)

Accessing local state

Within your servicer methods, you can access the state of the current instance via self.state (Python) or this.state (TypeScript class-based).

In TypeScript, if you're using the object literal syntax, state is passed as a parameter to your method.

async def send(
self,
context: WriterContext,
request: SendRequest,
) -> SendResponse:
message = request.message
self.state.messages.extend([message])
return SendResponse()
important

Only Writer, Transaction, and Workflow methods can modify state. Reader methods can only read state.

Accessing your state ID

You can access the ID of the current state instance via context.state_id (Python) or context.stateId (TypeScript):

async def get_id(
self,
context: ReaderContext,
request: GetIdRequest,
) -> GetIdResponse:
current_id = context.state_id
return GetIdResponse(id=current_id)

Making concurrent calls

You can make concurrent calls using asyncio.gather() (Python) or Promise.all() (TypeScript):

all_customer_balances: list[CustomerAccounts] = await asyncio.gather(
*[
customer_accounts(entry.value.decode())
for entry in customer_ids.entries
]
)
tip

Using concurrent calls can significantly improve performance when gathering data from multiple state instances.

Context constraints

Each context type constrains what methods you can call, enforcing Reboot's safety guarantees:

Context TypeCan CallCan Modify State
ReaderContextOnly reader methodsNo
WriterContextOnly reader methodsYes (own state only)
TransactionContextreader and writer methodsYes
WorkflowContextAny method typeYes
tip

Type checkers like mypy, Pyright, and tsc will catch violations of these constraints at compile time, helping you catch bugs before runtime.

Calling writers from other writers

Notice that WriterContext cannot call other writer methods directly. This is by design to prevent unsafe, partial updates in the case that one of the calls fails.

If you need to call multiple writer methods atomically, use a transaction method instead.

Scheduling writers from writers

The one exception to the "writers can't call writers" rule is through scheduled tasks. A writer can schedule another writer to execute later:

async def open(
self,
context: WriterContext,
) -> None:
self.state.balance = 0.0
await self.ref().schedule(
when=timedelta(seconds=1),
).interest(context)

This works because .schedule() creates a separate task that will execute independently, not as part of the current writer's execution. See Tasks for more details.