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.
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); } } }}Options
Section titled “Options”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. |
Result
Section titled “Result”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.
Same callable contract as mutations
Section titled “Same callable contract as mutations”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()clearsdataanderrorand setsisLoadingsynchronously, 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 intoerror().- Non-
Errorthrows are normalized tonew 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.
Related
Section titled “Related”- injectMutation — database writes, optimistic updates, and the full callable contract.