Releases: eneajaho/ngx-isr
v0.5.6
v.0.5.5
Features
- feat: allow
NgxIsrService
to be used in application code without bringing the whole library in the browser bundle - feat: separate the library into secondary entry points for server and browser
- [#37] fix: allow caching pages on different desk partitions (thanks to @alethyst)
BREAKING CHANGES:
Imports 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
andFileSystemCacheOptions
Things exported from ngx-isr/browser
:
NgxIsrService
Things exported from ngx-isr/models
:
CacheHandler
CacheISRConfig
(renamed fromISROptions
)CacheData
INgxIsrService
andNgxIsrState
ISRHandlerConfig
InvalidateConfig
RenderConfig
ServeFromCacheConfig
RouteISRConfig
v0.5.4
v0.5.3
Features
-
feat: Introduce
RouteISRConfig
interface for better type safety in route dataHow 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 calledbuildId
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
Breaking changes:
ISROptions
is being deprecated. UseCacheISRConfig
instead.
v0.5.2
v0.5.1
Changes:
- feat: Added extra field to isrService in order to save extra data if needed
How to use the extra field:
Example: Measure api calls timings
- Create an interceptor: UrlTimingsInterceptor
import { HttpHandler, HttpInterceptor, HttpRequest, HTTP_INTERCEPTORS } from '@angular/common/http';
import { Injectable, Provider } from '@angular/core';
import { NgxIsrService } from 'ngx-isr';
import { tap } from 'rxjs';
@Injectable()
export class UrlTimingsInterceptor implements HttpInterceptor {
constructor(private ngxIsrService: NgxIsrService) {}
intercept(request: HttpRequest<unknown>, next: HttpHandler) {
let timing1 = performance.now();
return next.handle(request).pipe(
tap(() => {
let timing2 = performance.now();
const currentExtra = this.ngxIsrService.getExtra();
const currentRequestsTimings = currentExtra['requestsTimings'] || [];
const currentUrlTiming = { url: request.url, timing: (timing2 - timing1).toFixed(2) + 'ms' };
if (currentRequestsTimings.find(t => t.url === request.url)) {
this.ngxIsrService.addExtra({
requestsTimings: currentRequestsTimings.map(t => t.url === request.url ? currentUrlTiming : t)
});
} else {
this.ngxIsrService.addExtra({ requestsTimings: [ ...currentRequestsTimings, currentUrlTiming ] });
}
})
);
}
}
export const HTTP_URL_TIMINGS_INTERCEPTOR_ISR: Provider = {
provide: HTTP_INTERCEPTORS,
useClass: UrlTimingsInterceptor,
multi: true,
}
- Import in AppServerModule providers
import { HTTP_URL_TIMINGS_INTERCEPTOR_ISR } from './url-timings.interceptor';
@NgModule({
...
providers: [HTTP_URL_TIMINGS_INTERCEPTOR_ISR] // --> Add provider here for the interceptor
})
export class AppServerModule {}
- Run the app and see that the timings for api calls will be included in the isr-state tag in the generated html.
Note: The code will be run only on server side, so it's code won't be bundled in the client side code 🎉
v0.5.0
Breaking Changes:
The invalidate method of IsrHandler now is converted to be a POST request.
server.post('/api/invalidate', async (req, res) =>
await isr.invalidate(req, res)
);
It accepts a body with the following structure:
{
token: string; // The secren token
urlsToInvalidate: string[]; // The urls to invalidate ex. ['/one', '/two']
}
Now you also need to add server.use(express.json());
in your server.ts file in order to parse the body of the request.
Changes:
* feat: added modifyCachedHtml and modifyGeneratedHtml callbacks to provide a mechanism to change html on the fly
* feat: Invalidate/regenerate multiple urls with one request
Thanks to @renatoaraujoc for contributing on the modifyCache feature!
v0.4.0
Now ngx-isr will support only projects in v15 and above. If you want to use it in older versions of Angular, please use v0.3.1.
The reason for this is because now we use ɵSERVER_CONTEXT
token in order to set the rendering context that now will be shown as: ng-server-context="ngx-isr"
. And this token is only available in v15 and above.
- Changes:
- chore: Updated the project to v15
- feat: Added server context provider
- feat: Added RedisCacheHandler class usage in the demo app (experimental)
- chore: Started to convert the demo app in a documentation page for the library
v0.3.1
Changes
- Added FileSystem CacheHandler
- Added
"@types/node": "^14.15.0"
to peerDeps required by FileSystemCacheHandler
Details:
Before v.0.3.1 we had the possibility to add custom cache handler to the ISR handler, and the default one was the InMemoryCacheHandler. So, every time the application booted it would save all the new cached pages in the memory, but this doesn't scale well, because the memory usage would increase a lot.
In v.0.3.1 we introduced a new cache handler called FileSystemCacheHandler
, that enables us to store the cached pages on server filesystem. But now, because we have access to the filesystem, we could have the possibility to prerender pages at build time, but also revalidate them on runtime, giving us the best of both worlds (static and dynamic).
How to configure FileSystemCacheHandler?
- Import it in server.ts
import { FileSystemCacheHandler } from 'ngx-isr';
- Create an instance of
FileSystemCacheHandler
const fsCacheHandler = new FileSystemCacheHandler({
cacheFolderPath: join(distFolder, '/cache'),
prerenderedPagesPath: distFolder,
addPrerendedPagesToCache: true, // set this to true in order to add prerendered pages with `prerenderer` to the cache
});
- Use the instance of the fsCacheHandler in the
ISRHandler
const isr = new ISRHandler({
...
cache: fsCacheHandler // here we override the default cache handler that is the InMemoryCacheHandler
});
- How to prerender at build time?
The same way we use the defaultprerenderer
, nothing else. Everything is supposed to work the same.
How this works?
FileSystemCacheHandler reads the prerenderedPagesPath
that you provide and reads all the folders in the browser folder, and checks if they have an index.html
inside (as this is the behavior when we generate static files with the prerenderer
).
If the index.html
exists in that folder, we add the folder name to the cache handler as the url and the index.html
content as the html.
Then we remove the found files in the folder because we don't want to have the default behavior of the angular universal (that serves the static files), because we want to override that behavior with the one of the ISR
.
Then we add those files that we found to the cache, so in a different place in the filesystem (to the path you provided in cacheFolderPath
).
Now, when a request is made, ISR will check if the cache has the url in it, if yes, it will serve it, otherwise it will server-side render it and if it has the revalidate
key provided, it will save it to cache.