Installation
Install the full stack or individual packages in a React project.
#React stack
# Full React stack
pnpm add @void-snippets/core @void-snippets/client @void-snippets/react
# Required peer dependencies
pnpm add axios @tanstack/react-query
# Only needed if you use socket hooks
pnpm add socket.io-client
# Only needed if you use the routing contract
pnpm add react-router#React peer dependency versions
| Peer | Minimum | Required for |
|---|---|---|
react | >=17.0.0 | All React hooks |
axios | ^1.6.0 | @void-snippets/client |
@tanstack/react-query | ^5.0.0 | createResourceHooks |
socket.io-client | >=4.6.0 | createSocketHooks |
react-router | >=7.0.0 | createRouteContract |
| TypeScript | ^5.4.0 | Everything |
#Angular stack
@void-snippets/angular does not use axios — it uses Angular's built-in HttpClient. Install the Angular package alongside core:
pnpm add @void-snippets/core @void-snippets/angular
# Required peer dependencies
pnpm add @tanstack/angular-query-experimental rxjs#Angular peer dependency versions
| Peer | Minimum | Required for |
|---|---|---|
@angular/core | >=19.0.0 | inject(), signal() |
@angular/common | >=19.0.0 | HttpClient |
rxjs | >=7.0.0 | Observable → Promise bridge |
@tanstack/angular-query-experimental | >=5.0.0 | createResourceInject |
| TypeScript | ^5.4.0 | Everything |
No axios, no
configure()call. Angular'sHttpClient— configured through interceptors inprovideHttpClient(...)— handles auth, CSRF, tracing, and retry.AngularResourceServicepicks up the interceptor chain automatically.
#One-time app setup
Do this once at your app's entry point, before any service is called.
1. Configure the HTTP client:
// main.ts
import axios from 'axios';
import { configure } from '@void-snippets/client';
configure(
axios.create({
baseURL: import.meta.env.VITE_API_URL,
headers: { 'Content-Type': 'application/json' },
})
);2. Wrap your app with QueryClientProvider:
// main.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient({
defaultOptions: { queries: { retry: 1, staleTime: 30_000 } },
});
root.render(
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
);#Angular one-time app setup
Do this once in app.config.ts, before any service is injected.
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient, withFetch, withInterceptors } from '@angular/common/http';
import { provideVoidAngular } from '@void-snippets/angular';
import { authInterceptor } from './core/auth.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
// withFetch() enables OS-level network cancellation when TanStack Query aborts a request.
provideHttpClient(
withFetch(),
withInterceptors([authInterceptor]),
),
// One-call setup — registers TanStack Angular Query with enterprise defaults.
provideVoidAngular({
queryClientConfig: {
defaultOptions: {
queries: { staleTime: 30_000, retry: 1 },
mutations: { retry: 0 },
},
},
}),
],
};There is no configure() call for Angular — auth, CSRF, and retry are wired once through provideHttpClient(...) interceptors, and every AngularResourceService subclass inherits them automatically.
#NestJS stack
@void-snippets/nestjs needs core plus whichever ORM your app already uses — it never installs an ORM for you:
pnpm add @void-snippets/core @void-snippets/nestjs
# plus whichever ORM you use
pnpm add typeorm @nestjs/typeorm # TypeORM
# or
pnpm add mongoose @nestjs/mongoose # Mongoose
# only if you validate request bodies with DTOs
pnpm add class-validator class-transformer#NestJS peer dependency versions
| Peer | Minimum | Required for |
|---|---|---|
@nestjs/common | >=10.0.0 | Everything |
typeorm + @nestjs/typeorm | >=0.3.0 | forTypeOrmResource |
mongoose + @nestjs/mongoose | >=7.0.0 | forMongooseResource |
class-validator + class-transformer | any recent | DTO body validation (optional) |
| TypeScript | ^5.4.0 | Everything |
class-validatorandclass-transformerare optional. You only need them once you attach a DTO class to a resource — the generated controller then validates the request body for you, with no globalValidationPipeto wire up yourself.
Build note: unlike the other packages,
@void-snippets/nestjsships CommonJS compiled withtsc, nottsup/esbuild. NestJS's dependency injection relies on a TypeScript feature (emitDecoratorMetadata) that esbuild-based bundlers don't emit.
#NestJS one-time app setup
Do this once in your root AppModule, before any feature module that registers a resource:
// app.module.ts
import { Module } from '@nestjs/common';
import { VoidResourceModule } from '@void-snippets/nestjs';
import { ContactsModule } from './contacts/contacts.module';
@Module({
imports: [
VoidResourceModule.forRoot({
cursorSecret: process.env.CURSOR_SECRET, // signs cursors so they can't be tampered with
maxLimit: 200, // hard ceiling on ?limit= for every resource
defaultLimit: 25, // page size when a request omits ?limit=
}),
ContactsModule,
// ...your other feature modules
],
})
export class AppModule {}forRoot() sets defaults shared by every resource in your app, so you configure them once instead of repeating the same options in every defineResource() call. Any option a resource sets for itself overrides the app-wide default.