Skip to content

injectConvex

injectConvex() returns the ConvexClient instance that provideConvex() registered. Use it for the cases the reactive helpers do not cover: one-shot queries, imperative mutations and actions from non-component code, and manual subscriptions you want to own yourself.

function injectConvex(options?: InjectConvexOptions): ConvexClient;
interface InjectConvexOptions {
/**
* Environment injector used to resolve the Convex client when creating
* helpers outside the current injection context.
*/
injectRef?: EnvironmentInjector;
}
Parameter Type Purpose
options.injectRef EnvironmentInjector Resolve the client from an explicit injector when calling outside an injection context.

The return value is the ConvexClient from convex/browser — the same instance every helper in this library uses, not a wrapper. Its full API is available: query, mutation, action, onUpdate, connectionState, and the underlying client (a BaseConvexClient).

todo-admin.service.ts
import { Injectable } from '@angular/core';
import { injectConvex } from 'convex-angular';
import { api } from './convex/api';
@Injectable({ providedIn: 'root' })
export class TodoAdminService {
// The very same ConvexClient instance provideConvex(...) registered under
// the CONVEX token. Throws if provideConvex(...) was never called.
private readonly convex = injectConvex();
/** One-shot query outside of any subscription. */
async count(): Promise<number> {
const todos = await this.convex.query(api.todos.list, { count: 100 });
return todos.length;
}
/** Imperative mutation, for code that is not a component interaction. */
async create(title: string): Promise<void> {
await this.convex.mutation(api.todos.create, { title });
}
/** Imperative action. */
async completeAll(): Promise<number> {
return this.convex.action(api.todos.completeAll, {});
}
/** Manual subscription; you own the unsubscribe. */
watch(onTodos: (titles: string[]) => void): () => void {
return this.convex.onUpdate(api.todos.list, { count: 100 }, (todos) => {
onTodos(todos.map((todo) => todo.title));
});
}
}

The client is created by provideConvex() and closed automatically when the owning injector is destroyed. Do not call close() on it yourself — the rest of the application shares the instance.

injectConvex() throws a focused setup error when provideConvex() was never called, instead of Angular’s generic NullInjectorError:

Could not find `CONVEX`. Make sure to call `provideConvex(...)` once in your root application providers (for example, in `app.config.ts`).

Calling it outside an injection context without an injectRef throws:

injectConvex() must be called from an injection context (for example, a component or service field initializer or constructor), or be given an explicit injector via the `injectRef` option to create it later from plain code.

During a server render the client is constructed with disabled: true. Direct calls to convex.query(), convex.mutation(), and convex.action() throw:

ConvexClient is disabled

convex.onUpdate() registration is a no-op there, and the convex.client getter throws. Check convex.disabled before using the client imperatively in code that can run on the server. See Server-side rendering.

const CONVEX: InjectionToken<ConvexClient>;

The injection token provideConvex() registers the client under. injectConvex() reads it, and you can inject it directly when you want plain Angular DI semantics:

import { inject } from '@angular/core';
import { CONVEX } from 'convex-angular';
export class TodoService {
private readonly convex = inject(CONVEX);
}

The difference is only in the failure mode: inject(CONVEX) produces Angular’s standard NullInjectorError when the provider is missing, while injectConvex() produces the focused message above. The token is also the seam to override in tests — provideConvexTesting() registers a MockConvexClient under it.