Aviary
Recipes

Headless SDK & TanStack Query

A framework-neutral client for the inbox API + SSE — fetch functions, a live subscribe(), and TanStack Query option factories that work in React and Vue. For frontends separate from the backend.

The <Inbox/> widget is React-specific. When your frontend lives in a separate repo, points at a different backend, or isn't React, reach for @dudousxd/nestjs-notifications-client — a headless, framework-neutral SDK: typed fetch methods, a live subscribe() over SSE, and TanStack Query option factories.

pnpm add @dudousxd/nestjs-notifications-client

On NestJS and want the client generated (and kept in lockstep with your routes)? Use codegen instead. This package is the hand-written, zero-config alternative.

The client

import { createNotificationsClient } from '@dudousxd/nestjs-notifications-client';

const client = createNotificationsClient({
  baseUrl: 'https://api.example.com/',                       // read API mounted at /notifications
  sseUrl: 'https://api.example.com/notifications/stream',    // optional live stream
  headers: { Authorization: `Bearer ${token}` },            // or credentials: 'include'
});

await client.list({ page: 1 });
await client.unread();
await client.unreadCount();
await client.markAsRead(id);
await client.markAllAsRead();
await client.remove(id);

It uses the global fetch; pass a fetch impl for non-browser runtimes.

Live updates with subscribe()

const stop = client.subscribe(({ count }) => {
  // `count` is set when the server pushes it in the SSE payload; otherwise re-fetch.
  if (count != null) setBadge(count);
  else client.unreadCount().then(setBadge);
});
// later: stop()

subscribe() is SSR-safe — when there's no EventSource (or no sseUrl), it returns a no-op unsubscribe, so it's safe to call during server rendering.

TanStack Query

The /tanstack subpath exports framework-neutral option factories — they return plain queryOptions/mutationOptions-shaped objects, so they drop into @tanstack/react-query and @tanstack/vue-query alike.

React
import { createNotificationsClient } from '@dudousxd/nestjs-notifications-client';
import {
  notificationKeys,
  notificationQueries,
  notificationMutations,
} from '@dudousxd/nestjs-notifications-client/tanstack';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';

const client = createNotificationsClient({ baseUrl: '/' });

function Bell() {
  const { data: count } = useQuery(notificationQueries.unreadCount(client));
  return <span>🔔 {count}</span>;
}

function useMarkRead() {
  const qc = useQueryClient();
  return useMutation({
    ...notificationMutations.markAsRead(client),
    onSuccess: () => qc.invalidateQueries({ queryKey: notificationKeys.all }),
  });
}
Vue
import { useQuery } from '@tanstack/vue-query';
import { notificationQueries } from '@dudousxd/nestjs-notifications-client/tanstack';

const { data } = useQuery(notificationQueries.list(client, { page: 1 }));

notificationKeys gives you the stable query keys for cache reads and invalidation (notificationKeys.all, .list(params), .unread(), .unreadCount()).

Which client should I use?

You are…Use
React app, want a drop-in UI<Inbox/>
Any frontend, want headless fns + TanStackthis package
On NestJS, want a generated typed clientcodegen

On this page