Skip to content

injectAction

injectAction() creates a reactive caller for a Convex action. Actions are the place for work that reaches beyond the database — calling third-party APIs, sending email, talking to an LLM — so they run outside a transaction and cannot be replayed by the client.

function injectAction<Action extends ActionReference>(
action: Action,
options?: ActionOptions<Action>,
): ActionResult<Action>;

ActionReference is FunctionReference<'action'>. As with mutations, call it from an injection context or pass an explicit injectRef.

send-email.component.ts
import { Component } from '@angular/core';
import { ConvexError, injectAction } from 'convex-angular';
import { api } from './convex/api';
@Component({
selector: 'app-send-email',
template: `
<button type="button" (click)="send()" [disabled]="sendEmail.isLoading()">Send welcome email</button>
@if (sendEmail.isSuccess()) {
<p>Queued as {{ sendEmail.data()?.id }}</p>
}
@if (sendEmail.error(); as error) {
<p role="alert">{{ error.message }}</p>
}
`,
})
export class SendEmailComponent {
readonly sendEmail = injectAction(api.emails.send, {
onSuccess: (result) => console.log('sent', result.id),
onError: (err) => console.error('send failed', err),
});
async send(): Promise<void> {
try {
// `run()` resolves with the action's return value and rejects on
// failure, exactly like `mutate()` does for mutations.
const { id } = await this.sendEmail.run({ to: 'user@example.com', subject: 'Welcome' });
console.log(id);
} catch (error) {
if (error instanceof ConvexError) {
console.error(error.data);
}
}
}
}

ActionOptions<Action>:

Name Type Default Description
injectRef EnvironmentInjector Injector used to resolve dependencies when the action is created outside the ambient injection context. The action’s lifetime is bound to that injector.
onSuccess (data: FunctionReturnType<Action>) => void Called after data is written, for the latest call only.
onError (err: Error) => void Called after error is written, for the latest call only. Receives the normalized Error.

ActionResult<Action> is the mutation surface with run() in place of mutate():

Name Type Description
run (args: Action['_args']) => Promise<FunctionReturnType<Action>> Runs the action. Resolves with its return value, rejects on failure.
data Signal<FunctionReturnType<Action> | undefined> Return value of the last successful call.
error Signal<Error | undefined> Error from the last failed call.
isLoading Signal<boolean> true while the latest call is in flight.
isSuccess Signal<boolean> true only when a call has completed, nothing is loading, and there is no error.
status Signal<ActionStatus> 'idle' | 'pending' | 'success' | 'error'.
reset () => void Clears data, error, and isLoading, and returns status to 'idle'.

ActionStatus and MutationStatus are the same union of four values.

run() and mutate() are the same function under two names — both come from one shared implementation. Everything documented in the callable contract applies unchanged:

  • run() clears data and error and sets isLoading synchronously, before awaiting.
  • Only the latest call writes state or fires callbacks; a superseded call still settles its own promise silently.
  • run() always rejects on failure even though the error is also mirrored into error().
  • Non-Error throws are normalized to new Error(String(err)), and that one instance is both stored and thrown.
  • reset() orphans in-flight calls.
  • After the owning scope is destroyed, calls still hit the network but touch no signals and fire no callbacks.
  • injectMutation — database writes, optimistic updates, and the full callable contract.