ngx-signal-query
v1.2.0
Published
Signal-first data fetching, caching, and mutations for Angular — inspired by TanStack Query.
Maintainers
Readme
ngx-signal-query
A tiny, signal-native library for data fetching, caching, and mutations in
Angular — inspired by TanStack Query. Built
directly on Angular Signals, with first-class RxJS support: your queryFn can
return an Observable (hello HttpClient) or a Promise.
Features
- 🚦 Signal-native — query state is exposed as signals (
data(),status(), …), no manual subscriptions. - 🗄️ Caching & deduplication — identical query keys share a single in-flight request and cache entry.
- ✏️ Mutations — with
onMutate/onSuccess/onErrorlifecycle for optimistic updates. - 🔁 Retries & polling — configurable
retryandrefetchInterval. - 🌍 Global indicators —
injectIsFetching()/injectIsMutating()for app-wide loading state. - 🪶 Lean — ESM,
sideEffects: false, no runtime deps beyondtslib.
Installation
npm install ngx-signal-queryRequires Angular 19+ (@angular/core is the only peer dependency).
Quick start
1. Provide the query client
// app.config.ts
import { ApplicationConfig } from '@angular/core'
import { provideHttpClient } from '@angular/common/http'
import { provideQueryClient } from 'ngx-signal-query'
export const appConfig: ApplicationConfig = {
providers: [provideHttpClient(), provideQueryClient()],
}2. Fetch data in a component
import { Component, inject } from '@angular/core'
import { HttpClient } from '@angular/common/http'
import { injectQuery } from 'ngx-signal-query'
type Todo = { id: number; title: string }
@Component({
selector: 'app-todos',
template: `
<p>status: {{ todos.status() }}</p>
@if (todos.data(); as data) {
<ul>
@for (todo of data; track todo.id) {
<li>{{ todo.title }}</li>
}
</ul>
}
<button (click)="todos.refetch()">Refetch</button>
`,
})
export class TodosComponent {
private readonly http = inject(HttpClient)
protected readonly todos = injectQuery(() => ({
queryKey: ['todos'],
queryFn: () => this.http.get<Todo[]>('/api/todos'),
}))
}Mutations
import {
mutationOptions,
injectMutation,
injectQueryClient,
} from 'ngx-signal-query'
const client = injectQueryClient()
const addTodo = injectMutation(() =>
mutationOptions({
mutationFn: (title: string) => http.post<Todo>('/api/todos', { title }),
onSuccess: () => client.invalidateQueries({ queryKey: ['todos'] }),
}),
)
// in a template
// <button (click)="addTodo.mutate('Buy milk')" [disabled]="addTodo.isPending()">Add</button>Documentation
📖 Read the docs — guides and examples.
