> ## Documentation index
> Fetch the complete documentation index at: https://upstash.com/docs/llms.txt
> Search it with GET https://upstash.com/docs/search?q=<query>.
> Use these to discover all available pages before exploring further.

# context.run

`context.run()` executes a piece of custom business logic as a workflow step.

It returns a `Promise`, so you can decide how steps execute:
- **Sequentially** by awaiting them one by one.
- **In parallel** by awaiting multiple steps together.

##  Arguments

<ParamField body="stepName" type="string">
    A unique identifier for the step.
</ParamField>

<ParamField body="stepFunction" type="function">
    The business logic to run inside this step.
</ParamField>

<ParamField body="options" type="object" optional>
    Flow control and retry configuration for this step only. Fields left unset fall back to the
    configuration the run was triggered with.

    See [step-level flow control](/workflow/features/flow-control#step-level-flow-control) and
    [retries](/workflow/features/retries#configure-retries-for-a-single-step) for details.

    <Expandable title="properties">
        <ParamField body="flowControl" type="object">
            Flow control configuration with `key`, `rate`, `parallelism` and `period`, the same shape as in
            [client.trigger](/workflow/basics/client/trigger). Replaces the run's flow control for this step.
        </ParamField>

        <ParamField body="retries" type="number">
            How many times the step is retried when it fails.
        </ParamField>

        <ParamField body="retryDelay" type="string">
            Delay between retries in milliseconds. Can use expressions like `"1000 * (1 + retried)"`.
        </ParamField>
    </Expandable>
</ParamField>

## Response

Each step can return a JSON-serializable value—anything from simple primitives to complex objects.

The value is **JSON-serialized** and automatically restored across requests.

<Warning>
    Avoid returning stateful resources such as database connections or file handles.

    Instead, return plain data (numbers, strings, arrays, objects) so the result can be safely persisted and restored across workflow executions.
</Warning>

## Usage

<CodeGroup>
    ```typescript Serial execution (TypeScript) highlight={6-8, 10-12}
    import { serve } from "@upstash/workflow/nextjs";

    export const { POST } = serve<string>(async (context) => {
      const input = context.requestPayload;

      const result1 = await context.run("step-1", async () => {
        return someWork(input);
      });

      await context.run("step-2", async () => {
        someOtherWork(result1);
      });
    });

    ```

    ```typescript Parallel execution (TypeScript)
    import { serve } from "@upstash/workflow/nextjs"

    export const { POST } = serve<string>(
      async (context) => {
        const input = context.requestPayload;

        const promise1 = context.run("step-1", async () => {
          return someWork(input);
        });

        const promise2 = context.run("step-2", async () => {
          return someOtherWork(input);
        });

        await Promise.all([promise1, promise2]);
      },
    );
    ```

    ```python Serial execution (Python)
    from fastapi import FastAPI
    from upstash_workflow.fastapi import Serve
    from upstash_workflow import AsyncWorkflowContext

    app = FastAPI()
    serve = Serve(app)

    @serve.post("/api/example")
    async def example(context: AsyncWorkflowContext[str]) -> None:
        input = context.request_payload

        async def _step1():
            return some_work(input)

        result1 = await context.run("step-1", _step1)

        async def _step2():
            return some_other_work(result1)

        await context.run("step-2", _step2)

    ```
</CodeGroup>

<Info>
    Because results are JSON-serialized, **class instances are restored as plain objects**.
    This means instance methods will not be available unless you explicitly rehydrate the object.

    To fix this, you can recreate the instance using Object.assign() or a custom factory:
    ```typescript
   export const { POST } = serve(
    async (context) => {

       let user = await context.run("step-1", async () => {
         // 👇 Return a class instance from step
         return new User("John Doe", "john.doe@example.com");
       });

       // 👇 Properties are accessible by default
       console.log(user.name)

       // 👇 Create a Proper Instance with Object.assign()
       user = Object.assign(new User(), user);

       await context.run("greet", async () => {
         // 👇 Now instance methods are available as well
         console.log(user.greet());
       });
     }
   );
    ```
</Info>
