# Invoking functions directly
Source: https://www.inngest.com/docs/guides/invoking-functions-directly

Inngest's `step.invoke()` function provides a powerful tool for calling functions directly within your event-driven system. It differs from traditional event-driven triggers, offering a more direct, RPC-like approach. This encourages a few key benefits:

- Allows functions to call and receive the result of other functions
- Naturally separates your system into reusable functions that can spread across process boundaries
- Allows use of synchronous interaction between functions in an otherwise-asynchronous event-driven architecture, making it much easier to manage functions that require immediate outcomes

## Invoking another function

<GuideSelector
  options={[
    { key: "typescript", title: "TypeScript" },
    { key: "go", title: "Go" },
    { key: "python", title: "Python" },
  ]}
>

<GuideSection show="typescript">

<Callout>
### When should I invoke?

Use `step.invoke()` in tasks that need specific settings like concurrency limits. Because it runs with its own configuration,
distinct from the invoker's, you can provide a tailored configuration for each function.

If you don't need to define granular configuration or if your function won't be reused across app boundaries, use `step.run()` for simplicity.
</Callout>

```ts
// Some function we'll call
inngest.createFunction(
  { id: "compute-square" },
  { event: "calculate/square" },
  async ({ event }) => {
    return { result: event.data.number * event.data.number }; // Result typed as { result: number }
  }
);

// In this function, we'll call `computeSquare`
inngest.createFunction(
  { id: "main-function" },
  { event: "main/event" },
  async ({ step }) => {
    await step.invoke("compute-square-value", {
      function: computeSquare,
      data: { number: 4 }, // input data is typed, requiring input if it's needed
    });

    return `Square of 4 is ${square.result}.`; // square.result is typed as number
  }
);
```

In the above example, our `mainFunction` calls `computeSquare` to retrieve the resulting value. `computeSquare` can now be called from here or any other process connected to Inngest.

## Referencing another Inngest function

If a function exists in another app, you can create a reference that can be invoked in the same manner as the local `computeSquare` function above.

```ts
// @/inngest/computeSquare.ts
// Create a reference to a function in another application.
computeSquare = referenceFunction({
  appId: "my-python-app",
  functionId: "compute-square",
  // Schemas are optional, but provide types for your call if specified
  schemas: {
    data: z.object({
      number: z.number(),
    }),
    return: z.object({
      result: z.number(),
    }),
  },
});
```

```ts
// square.result is typed as a number
await step.invoke("compute-square-value", {
  function: computeSquare,
  data: { number: 4 }, // input data is typed, requiring input if it's needed
});
```

References can also be used to invoke local functions without needing to import them (and their dependencies) directly. This can be useful for frameworks like Next.js where edge and serverless handlers can be mixed together and require different sets of dependencies.

```ts
// Import only the type

inngest.createFunction(
  { id: "main-function" },
  { event: "main/event" },
  async ({ step }) => {
    await step.invoke("compute-square-value", {
      function: referenceFunction<typeof computeSquare>({
        functionId: "compute-square",
      }),
      data: { number: 4 }, // input data is still typed
    });

    return `Square of 4 is ${square.result}.`; // square.result is typed as number
  }
);
```

For more information on referencing functions, see [TypeScript -> Referencing Functions](/docs/functions/references).

</GuideSection>

<GuideSection show="go">

<Callout>
### When should I invoke?

Use `step.Invoke()` in tasks that need specific settings like concurrency limits. Because it runs with its own configuration,
distinct from the invoker's, you can provide a tailored configuration for each function.

If you don't need to define granular configuration or if your function won't be reused across app boundaries, use `step.Run()` for simplicity.
</Callout>

```go
import (
	"context"
	"fmt"

	"github.com/inngest/inngestgo"
	"github.com/inngest/inngestgo/step"
)

// Some function we'll call
inngestgo.CreateFunction(
	client,
	inngestgo.FunctionOpts{Name: "compute-square"},
	inngestgo.EventTrigger("calculate/square", nil),
	func(ctx context.Context, input inngestgo.Input[map[string]any]) (any, error) {
		number, ok := input.Event.Data["number"].(float64)
		if !ok {
			return nil, fmt.Errorf("invalid number")
		}

		return map[string]any{
			"result": int(number * number),
		}, nil
	},
)

// In this function, we'll call the compute-square function
inngestgo.CreateFunction(
	client,
	inngestgo.FunctionOpts{Name: "main-function"},
	inngestgo.EventTrigger("main/event", nil),
	func(ctx context.Context, input inngestgo.Input[map[string]any]) (any, error) {
		square, err := step.Invoke(ctx, "compute-square-value", &inngestgo.InvokeOpts{
			Function: "compute-square",
			Data: map[string]any{
				"number": 4,
			},
		})
		if err != nil {
			return nil, err
		}

		result := square.Data["result"].(int)
		return fmt.Sprintf("Square of 4 is %d.", result), nil
	},
)

```

In the above example, our `mainFunction` calls `computeSquare` to retrieve the resulting value. `computeSquare` can now be called from here or any other process connected to Inngest.

</GuideSection>
<GuideSection show="python">

<Callout>
### When should I invoke?

Use `step.invoke()` in tasks that need specific settings like concurrency limits. Because it runs with its own configuration,
distinct from the invoker's, you can provide a tailored configuration for each function.

If you don't need to define granular configuration or if your function won't be reused across app boundaries, use `step.run()` for simplicity.
</Callout>

```py
import inngest
from src.inngest.client import inngest_client