npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

nest-sirv

v0.1.1

Published

NestJS static assets module powered by sirv

Downloads

329

Readme

nest-sirv

NestJS static assets module powered by sirv — a drop-in alternative to @nestjs/serve-static for serving SPA frontends next to a high-RPS API.

Both this module and @nestjs/serve-static register their handler in onModuleInit, i.e. after the controller router. So a request that matches a controller route never reaches the static layer — the differences below apply to the traffic that does reach it: static assets, the SPA fallback, and unmatched paths.

Why

  • No filesystem hit per request. In production mode sirv scans the directory once at startup and keeps file metadata (paths, sizes, headers, etags) in memory. Serving a file is a Map lookup + createReadStreamexpress.static does a stat() syscall on every request it handles.
  • True route exclusion (also a safety boundary). exclude guards the static handler itself: an excluded path costs a string comparison and is passed straight to the next handler. In @nestjs/serve-static, exclude only suppresses the SPA fallback — express.static stays mounted, so a file placed under an excluded prefix (e.g. public/api/secret.txt) is actually served to an unmatched request.
  • SPA fallback built in (single: true) — no wildcard render handler, no path-to-regexp compiled per request. Note: sirv only falls back to index.html for paths that don't look like a file; a miss on something.js returns 404 rather than HTML (avoids soft-404s). Set sirv: { ignores: false } for the serve-static behaviour of falling back on every miss.
  • Precompressed assets. Put bundle.js.br / bundle.js.gz next to bundle.js and sirv serves them automatically based on Accept-Encoding.

Measured in isolation (bare http server, no Nest) against @nestjs/serve-static's express stack: ~+11% RPS on plain files, ~+32% on SPA fallback. In a real app the static layer only handles asset/SPA/unmatched traffic, so the end-to-end win is proportional to how much of that you serve.

Install

npm install nest-sirv

Requires the Express platform adapter (@nestjs/platform-express).

Usage

import { Module } from '@nestjs/common';
import { SirvModule } from 'nest-sirv';
import { join } from 'node:path';

@Module({
    imports: [
        SirvModule.forRoot({
            rootPath: join(__dirname, '..', 'frontend'),
            exclude: ['/api'],
            sirv: {
                brotli: true,
                gzip: true,
                dev: process.env.NODE_ENV === 'development',
            },
        }),
    ],
})
export class AppModule {}

Conditional registration (e.g. API-only instances) works the same way as with serve-static:

ConditionalModule.registerWhen(
    SirvModule.forRoot({ rootPath, exclude: ['/api'] }),
    () => process.env.DISABLE_FRONTEND !== 'true',
),

forRootAsync with useFactory / useClass / useExisting is available as well.

Options

| Option | Type | Description | | ----------- | -------------- | -------------------------------------------------------------------------------------------------------------- | | rootPath | string | Required. Absolute path to the static assets directory. | | serveRoot | string | URL prefix to mount under (default /). | | exclude | string[] | Path prefixes that bypass static serving entirely. Matched on segment boundaries: '/api' excludes /api and /api/x, but not /api-docs. | | sirv | sirv.Options | Passed through to sirv. Module defaults: etag: true, single: true, dev: false. |

See the sirv options reference for maxAge, immutable, brotli, gzip, extensions, dotfiles and friends.

Caveats

  • Production mode snapshots the directory at startup. Files added after boot are not served until restart. This is exactly what you want for a frontend baked into a container image; set sirv.dev = true if the directory changes at runtime (local development).
  • Express adapter only. Fastify support is not implemented (the module throws a descriptive error).

License

MIT