NestJS
Mount better-push as a Nest module, with forRoot and forRootAsync.
import { Module } from "@nestjs/common";
import { BetterPushModule } from "@better-push/core/nestjs";
import { push } from "./push";
@Module({
imports: [BetterPushModule.forRoot({ push, basePath: "/api/push" })],
})
export class AppModule {}import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
const app = await NestFactory.create(AppModule);
await app.listen(3000);How it mounts
The module routes through NestModule.configure() middleware, not a
controller. A controller would need a decorated class with a route per endpoint,
which would drag experimentalDecorators and emitDecoratorMetadata into
better-push's own tsconfig - changing how the entire package compiles, for every
app that never touches Nest.
The middleware is applied to every route and decides for itself whether a path
belongs to better-push, forwarding everything else with next(). Nest's
wildcard route syntax changed between major versions (path-to-regexp v6 to v8);
a prefix check works on both, and is the same check the router would make.
@nestjs/common and @nestjs/core are optional peer dependencies and the
entry imports neither. Everything Nest hands it is typed structurally.
forRootAsync
When the instance depends on something Nest owns - a config service, a database connection - build it in a factory:
@Module({
imports: [
ConfigModule,
BetterPushModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
basePath: "/api/push",
useFactory: (config: ConfigService) =>
betterPush({
database: postgresAdapter(config.getOrThrow("DATABASE_URL")),
providers: [webPush({ vapid: config.getOrThrow("VAPID") })],
session: betterAuthSession(auth),
basePath: "/api/push",
}),
}),
],
})
export class AppModule {}basePath is required here and must match the one in the config: the
instance the factory returns does not exist when routing is set up, so its own
basePath cannot be read. The module says so rather than guessing.
The resolved instance is also provided under the BETTER_PUSH token, so the
rest of your app can inject it:
import { BETTER_PUSH } from "@better-push/core/nestjs";
constructor(@Inject(BETTER_PUSH) private readonly push: BetterPush) {}Caveats
Global prefix. app.setGlobalPrefix("api") applies to controllers, and this
is middleware. Include the prefix in basePath yourself:
app.setGlobalPrefix("api");
BetterPushModule.forRoot({ push, basePath: "/api/push" });Body parser. NestFactory.create(AppModule, { bodyParser: false }) is
recommended but not required - the bridge re-serializes a body a parser has
already consumed. With the parser on, malformed JSON is rejected by Nest before
better-push sees it, so that one case answers with Nest's error rather than
better-push's.
Fastify is untested. The middleware path is exercised on the Express
platform. FastifyRequest.raw/FastifyReply.raw are the objects it would need,
and nothing here has been run against them - so it is documented as untested
rather than claimed as supported.
Two instances in one process are fine: each registration gets its own module
class closing over its own mount, so a second forRoot cannot redirect the
first.