Incremental Static Regeneration (ISR) enables developers and content editors to use static-generation on a per-page basis, without needing to rebuild the entire site.
Enea Jahollari
Migrate to the new library by using:
ng update ngx-isr@0.6.0
The migration will:
@rx-angular/isr
library latest versionngx-isr
library from package.jsonAutomatically!
A library that enables Angular Universal applications to generate static pages at runtime and then update them incrementally on demand or on a schedule.
Install npm package
npm install ngx-isr
# or
yarn add ngx-isr
# or
pnpm add ngx-isr
Initialize ISRHandler
inside server.ts
const isr = new ISRHandler({
indexHtml,
invalidateSecretToken: 'MY_TOKEN', // replace with env secret key ex. process.env.REVALIDATE_SECRET_TOKEN
enableLogging: !environment.production
});
Add invalidation url handler
server.use(express.json());
server.post("/api/invalidate", async (req, res) => await isr.invalidate(req, res));
Replace Angular default server side rendering with ISR rendering
Replace
server.get('*',
(req, res) => {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
}
);
with
server.get('*',
// Serve page if it exists in cache
async (req, res, next) => await isr.serveFromCache(req, res, next),
// Server side render the page and add to cache if needed
async (req, res, next) => await isr.render(req, res, next),
);
You can also pass providers
to each of the ISRHandler
methods.
server.get('*',
...
async (req, res, next) => await isr.render(req, res, next, {
providers: [
{ provide: APP_BASE_HREF, useValue: req.baseUrl }, // <-- Needs to be provided when passing providers
{ provide: CUSTOM_TOKEN, useValue: 'Hello from ISR' },
CustomService
]
}),
);
It is also possible to pass a modifyCachedHtml
or modifyGeneratedHtml
callbacks to the ISRHandler
methods.
These methods provide a way to modify the html served from cache or the html that is generated on the fly.
Important: Use these methods with caution as the logic written can increase the processing time.
server.get('*',
// Serve page if it exists in cache
async (req, res, next) => await isr.serveFromCache(req, res, next, {
modifyCachedHtml: (req, cachedHtml) => {
return `${cachedHtml}<!-- Hello, I'm a modification to the original cache! -->`;
}
}),
// Server side render the page and add to cache if needed
async (req, res, next) => await isr.render(req, res, next, {
modifyGeneratedHtml: (req, html) => {
return `${html}<!-- Hello, I'm modifying the generatedHtml before caching it! -->`
}
}),
);
ISRHandler provides APP_BASE_HREF
by default. And if you want pass providers
into the methods of ISRHandler, you will also have to provide APP_BASE_HREF
token.
revalidate
key in route dataExample:
{
path: "example",
component: ExampleComponent,
data: { revalidate: 5 },
}
NOTE: Routes that don't have revalidate key in data won't be handled by ISR. They will fallback to Angular default server side rendering pipeline.
NgxIsrModule
in your AppServerModule
or provide provideISR
in your AppServerModule
providers.Or, if you are in a standalone app, you can register the providers in your app.config.server.ts
file.
NgxIsrModule
import { NgxIsrModule } from 'ngx-isr/server'; // <-- Import module from library
@NgModule({
imports: [
...
NgxIsrModule.forRoot() // <-- Use it in module imports
]
})
export class AppServerModule {}
provideISR
functionimport { provideISR } from 'ngx-isr/server';
@NgModule({
providers: [
provideISR() // <-- Use it in module providers
]
})
export class AppServerModule {}
provideISR
function in standalone appimport { provideISR } from 'ngx-isr/server';
const serverConfig: ApplicationConfig = {
providers: [
provideServerRendering(),
provideISR() // <-- Use it in config providers
],
};
When registering the providers, NgxIsrService
will be initialized and will start to listen to route changes, only on the server side, so the browser bundle won't contain any extra code.
NgxIsrService
to be used in application code without bringing the whole library in the browser bundleImports now should be done from ngx-isr/server
and ngx-isr/browser
instead of ngx-isr
;
// Before
import { NgxIsrModule } from 'ngx-isr';
// After
import { NgxIsrModule } from 'ngx-isr/server';
// Before
import { provideISR } from 'ngx-isr';
// After
import { provideISR } from 'ngx-isr/server';
Things exported from ngx-isr/server
:
NgxIsrModule
provideISR
ISRHandler
FileSystemCacheHandler
and FileSystemCacheOptions
Things exported from ngx-isr/browser
:
NgxIsrService
Things exported from ngx-isr/models
:
CacheHandler
CacheISRConfig
(renamed from ISROptions
)CacheData
INgxIsrService
and NgxIsrState
ISRHandlerConfig
InvalidateConfig
RenderConfig
ServeFromCacheConfig
RouteISRConfig
feat: Introduce RouteISRConfig
interface for better type safety in route data
How to use it?
const routes: Rotues = [{
path: 'home',
component: HomeComponent,
data: { revalidate: 0 } as RouteISRConfig // 👈 Add type to route data
}];
feat: Added build id support
What is it and why do we need it?
The build id is a unique identifier that is generated for each build. It is used to invalidate the cache when a new build is deployed. So, when a new build is deployed, every page that will be requested will be server-rendered again and not served from the cache. This way, the users will always get the latest version of the application.
Useful when you have an external cache handler like Redis.
How to use it?
To use it, you need to pass the build id to the ISRHandler
constructor.
Angular itself doesn't generate a build id. But we can generate it using the environment file.
What we can do is to set field in the environment file called buildId
and set it to: new Date().getTime(),
.
Ex. environment.ts:
export const environment = {
production: false,
buildId: new Date().getTime() + '', // We need to convert it to string because the buildId is a string
};
This way we will have a unique build id for each build because the buildId will evaluated at build time. Then, we pass the build id to the ISRHandler constructor.
Ex. server.ts:
import { environment } from './src/environments/environment';
const isr = new ISRHandler({
.. other options
buildId: environment.buildTimestamp // Pass the build id
});
fix: Fixed a bug where the cache was not invalidated when the build id changed
ISROptions
is being deprecated. Use CacheISRConfig
instead.provideISR
provider functionMIT
We serve cookies on this site to analyze traffic, remember your preferences, and optimize your experience.