Documentation Index
Fetch the complete documentation index at: https://mintlify.com/SaulMoro/ngrx-rtk-query/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Code splitting allows you to lazy load API definitions in feature modules, reducing initial bundle size and improving application performance. ngrx-rtk-query supports multiple patterns for code splitting.
When to Use Code Splitting
Code splitting is beneficial when:
- You have multiple feature modules with different API endpoints
- Your application has distinct user flows (e.g., admin, user, public)
- You want to reduce initial bundle size
- Features are used independently or conditionally
Basic Code Splitting
Lazy load API definitions with Angular’s route-based code splitting:
Create feature API
import { createApi, fetchBaseQuery } from 'ngrx-rtk-query';
export interface Post {
id: number;
name: string;
content: string;
}
export const postsApi = createApi({
reducerPath: 'postsApi',
baseQuery: fetchBaseQuery({ baseUrl: 'https://posts.api.com' }),
tagTypes: ['Posts'],
endpoints: (build) => ({
getPosts: build.query<Post[], void>({
query: () => '/posts',
providesTags: ['Posts'],
}),
addPost: build.mutation<Post, Partial<Post>>({
query: (body) => ({
url: '/posts',
method: 'POST',
body,
}),
invalidatesTags: ['Posts'],
}),
}),
});
export const { useGetPostsQuery, useAddPostMutation } = postsApi;
Provide API in lazy route
import { Route } from '@angular/router';
import { provideStoreApi } from 'ngrx-rtk-query';
import { postsApi } from './api';
export const POSTS_ROUTES: Route[] = [
{
path: '',
providers: [provideStoreApi(postsApi)],
children: [
{
path: '',
loadComponent: () => import('./posts-list.component')
.then((c) => c.PostsListComponent),
},
{
path: ':id',
loadComponent: () => import('./post-details.component')
.then((c) => c.PostDetailsComponent),
},
],
},
];
Lazy load in app routes
import { Route } from '@angular/router';
export const appRoutes: Route[] = [
{
path: 'posts',
loadChildren: () => import('./features/posts/routes')
.then((r) => r.POSTS_ROUTES),
},
{
path: 'users',
loadChildren: () => import('./features/users/routes')
.then((r) => r.USERS_ROUTES),
},
];
The API is only loaded when the user navigates to the /posts route.
Multiple APIs with Different Base URLs
When you have multiple APIs with different base URLs, provide each API in its feature route:
Posts API
Users API
Routes
export const postsApi = createApi({
reducerPath: 'postsApi',
baseQuery: fetchBaseQuery({
baseUrl: 'https://posts-api.example.com'
}),
endpoints: (build) => ({
getPosts: build.query<Post[], void>({
query: () => '/posts',
}),
}),
});
export const usersApi = createApi({
reducerPath: 'usersApi',
baseQuery: fetchBaseQuery({
baseUrl: 'https://users-api.example.com'
}),
endpoints: (build) => ({
getUsers: build.query<User[], void>({
query: () => '/users',
}),
}),
});
export const appRoutes: Route[] = [
{
path: 'posts',
providers: [provideStoreApi(postsApi)],
loadChildren: () => import('./features/posts/routes')
.then((r) => r.POSTS_ROUTES),
},
{
path: 'users',
providers: [provideStoreApi(usersApi)],
loadChildren: () => import('./features/users/routes')
.then((r) => r.USERS_ROUTES),
},
];
Each API must have a unique reducerPath to avoid conflicts.
Same Base URL with injectEndpoints
For APIs sharing the same base URL, use RTK Query’s injectEndpoints() pattern:
Create base API
import { createApi, fetchBaseQuery } from 'ngrx-rtk-query';
// Create base API with empty endpoints
export const baseApi = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({ baseUrl: 'https://api.example.com' }),
tagTypes: ['Posts', 'Users', 'Comments'],
endpoints: () => ({}),
});
Inject endpoints in features
import { baseApi } from '@/core/api';
export const postsApi = baseApi.injectEndpoints({
endpoints: (build) => ({
getPosts: build.query<Post[], void>({
query: () => '/posts',
providesTags: ['Posts'],
}),
addPost: build.mutation<Post, Partial<Post>>({
query: (body) => ({
url: '/posts',
method: 'POST',
body,
}),
invalidatesTags: ['Posts'],
}),
}),
});
export const { useGetPostsQuery, useAddPostMutation } = postsApi;
Provide base API once
import { ApplicationConfig } from '@angular/core';
import { provideStoreApi } from 'ngrx-rtk-query';
import { baseApi } from './core/api';
export const appConfig: ApplicationConfig = {
providers: [
provideStore(),
provideStoreApi(baseApi), // Only provide base API
],
};
Use in lazy-loaded components
features/posts/posts-list.component.ts
import { Component } from '@angular/core';
import { useGetPostsQuery } from './api';
@Component({
template: `
@for (post of postsQuery.data(); track post.id) {
<div>{{ post.name }}</div>
}
`,
})
export class PostsListComponent {
// Hook is lazy-loaded with component
postsQuery = useGetPostsQuery();
}
With injectEndpoints(), the base API reducer is loaded upfront, but endpoint implementations are lazy-loaded per feature.
Eager vs Lazy Loading
Eager Loading
Lazy Loading
Load API immediately at app startup:import { provideStoreApi } from 'ngrx-rtk-query';
import { postsApi } from './posts/api';
export const appConfig: ApplicationConfig = {
providers: [
provideStore(),
provideStoreApi(postsApi), // Loaded immediately
],
};
Use when:
- API is used across the entire app
- Initial bundle size is not a concern
- API must be available before first render
Load API only when needed:import { provideStoreApi } from 'ngrx-rtk-query';
import { postsApi } from './api';
export const POSTS_ROUTES: Route[] = [
{
path: '',
providers: [provideStoreApi(postsApi)], // Lazy loaded
loadComponent: () => import('./posts-list.component')
.then((c) => c.PostsListComponent),
},
];
Use when:
- API is feature-specific
- Optimizing initial bundle size
- Feature may not be accessed by all users
Preloading Strategies
Combine lazy loading with Angular’s preloading strategies:
import { ApplicationConfig } from '@angular/core';
import { provideRouter, PreloadAllModules } from '@angular/router';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(
appRoutes,
// Preload all lazy routes after initial render
withPreloading(PreloadAllModules)
),
],
};
Custom Preload Strategy
Create a selective preloading strategy:
import { Injectable } from '@angular/core';
import { PreloadingStrategy, Route } from '@angular/router';
import { Observable, of, timer } from 'rxjs';
import { mergeMap } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class SelectivePreloadingStrategy implements PreloadingStrategy {
preload(route: Route, load: () => Observable<any>): Observable<any> {
if (route.data?.['preload']) {
// Delay preload by 2 seconds
return timer(2000).pipe(mergeMap(() => load()));
}
return of(null);
}
}
export const appRoutes: Route[] = [
{
path: 'posts',
data: { preload: true }, // Enable preloading
loadChildren: () => import('./features/posts/routes')
.then((r) => r.POSTS_ROUTES),
},
];
Shared APIs Across Features
Share API instances across multiple features:
Create shared API
export const sharedApi = createApi({
reducerPath: 'sharedApi',
baseQuery: fetchBaseQuery({ baseUrl: 'https://api.example.com' }),
tagTypes: ['Shared'],
endpoints: (build) => ({
getConfig: build.query<Config, void>({
query: () => '/config',
}),
}),
});
export const { useGetConfigQuery } = sharedApi;
Provide in root
import { provideStoreApi } from 'ngrx-rtk-query';
import { sharedApi } from './shared/api';
export const appConfig: ApplicationConfig = {
providers: [
provideStore(),
provideStoreApi(sharedApi), // Available everywhere
],
};
Use in any feature
features/posts/posts-list.component.ts
import { useGetConfigQuery } from '@/shared/api';
import { useGetPostsQuery } from './api';
export class PostsListComponent {
configQuery = useGetConfigQuery(); // Shared API
postsQuery = useGetPostsQuery(); // Feature API
}
Bundle Analysis
Analyze your bundle to verify code splitting:
# Build with stats
ng build --stats-json
# Analyze with webpack-bundle-analyzer
npx webpack-bundle-analyzer dist/stats.json
Look for:
- API definitions in separate chunks
- Reduced main bundle size
- Lazy-loaded feature chunks
Reduced Initial Bundle Size
- Faster initial page load
- Improved Time to Interactive (TTI)
- Better Lighthouse scores
On-Demand Loading
- Load only what’s needed
- Smaller cache footprint
- Faster subsequent navigations
Better Caching
- Unchanged features remain cached
- More granular cache invalidation
Additional HTTP Requests
- Lazy chunks require network requests
- Mitigated by HTTP/2 multiplexing
- Can use preloading strategies
Complexity
- More route configuration
- Need to manage multiple APIs
- Requires planning
Route Transition Delay
- First navigation to lazy route loads chunk
- Mitigated by preloading
- Use loading states
Best Practices
Use injectEndpoints for shared base URLs
Don’t create multiple APIs for the same backend:// ✅ Good - single API, injected endpoints
const baseApi = createApi({ reducerPath: 'api', ... });
const postsApi = baseApi.injectEndpoints({ ... });
// ❌ Bad - multiple APIs, same base URL
const postsApi = createApi({ baseUrl: 'https://api.com', ... });
const usersApi = createApi({ baseUrl: 'https://api.com', ... });
Provide APIs at the correct level
- Root-level: Shared across entire app
- Feature route: Used only in that feature
- Component: Very rare, usually not recommended
Use unique reducerPath for each API
const postsApi = createApi({ reducerPath: 'postsApi', ... });
const usersApi = createApi({ reducerPath: 'usersApi', ... });
Consider preloading strategies
Balance initial load time with user experience:
- Critical features: Eager load
- Common features: Preload
- Rare features: Lazy load
Monitor bundle sizes
Regularly analyze bundles to ensure code splitting is effective.
Troubleshooting
API not available in lazy route
Ensure provideStoreApi() is in the route’s providers array.
// ✅ Correct
{
path: 'posts',
providers: [provideStoreApi(postsApi)],
loadChildren: () => import('./posts/routes'),
}
// ❌ Wrong - missing providers
{
path: 'posts',
loadChildren: () => import('./posts/routes'),
}
Duplicate API registrations
If you see errors about duplicate reducerPath:
- Check that each API has a unique
reducerPath
- Ensure you’re not providing the same API multiple times
- Use
injectEndpoints() for shared base URLs
Slow initial route navigation
If lazy routes load slowly:
- Use a preloading strategy
- Show loading indicators during route transitions
- Consider eager loading frequently-used features