|
| 1 | +# @bbv/nestjs-plugins |
| 2 | + |
| 3 | +Composable NestJS plugin ecosystem by [BlackBox Vision](https://github.com/BlackBoxVision). Each module is a self-contained feature with its own Prisma schema, feature flags, and provider abstractions — like NestJS plugins that bring their own DB tables, config, and toggleable capabilities. |
| 4 | + |
| 5 | +Build a production-ready NestJS API by composing modules: `AuthModule`, `NotificationModule`, `StorageModule` — each bringing its schema, migrations, and feature set. |
| 6 | + |
| 7 | +## Packages |
| 8 | + |
| 9 | +### Tier 1 — Plugin Modules (own Prisma schema + feature flags) |
| 10 | + |
| 11 | +| Package | Description | Version | |
| 12 | +|---------|-------------|---------| |
| 13 | +| [`@bbv/nestjs-auth`](./packages/nestjs-auth) | Authentication, social login, organizations, RBAC | [](https://www.npmjs.com/package/@bbv/nestjs-auth) | |
| 14 | +| [`@bbv/nestjs-notifications`](./packages/nestjs-notifications) | Multi-channel notifications (email, in-app, SMS) | [](https://www.npmjs.com/package/@bbv/nestjs-notifications) | |
| 15 | +| [`@bbv/nestjs-storage`](./packages/nestjs-storage) | File storage with provider abstraction (S3, Firebase, DO Spaces, Local) | [](https://www.npmjs.com/package/@bbv/nestjs-storage) | |
| 16 | +| [`@bbv/nestjs-audit-log`](./packages/nestjs-audit-log) | Automatic audit logging with Prisma middleware | [](https://www.npmjs.com/package/@bbv/nestjs-audit-log) | |
| 17 | + |
| 18 | +### Tier 2 — Utility Packages |
| 19 | + |
| 20 | +| Package | Description | Version | |
| 21 | +|---------|-------------|---------| |
| 22 | +| [`@bbv/nestjs-prisma`](./packages/nestjs-prisma) | Prisma service shell, lifecycle management, test utilities | [](https://www.npmjs.com/package/@bbv/nestjs-prisma) | |
| 23 | +| [`@bbv/nestjs-pagination`](./packages/nestjs-pagination) | Pagination DTOs, helpers, and Swagger decorators | [](https://www.npmjs.com/package/@bbv/nestjs-pagination) | |
| 24 | +| [`@bbv/nestjs-response`](./packages/nestjs-response) | API response wrapper, transform interceptor, exception filter | [](https://www.npmjs.com/package/@bbv/nestjs-response) | |
| 25 | + |
| 26 | +## Quick Start |
| 27 | + |
| 28 | +```bash |
| 29 | +npm install @bbv/nestjs-prisma @bbv/nestjs-auth @bbv/nestjs-storage @bbv/nestjs-notifications |
| 30 | +``` |
| 31 | + |
| 32 | +```typescript |
| 33 | +// app.module.ts |
| 34 | +import { Module } from '@nestjs/common'; |
| 35 | +import { ConfigModule, ConfigService } from '@nestjs/config'; |
| 36 | +import { PrismaModule } from '@bbv/nestjs-prisma'; |
| 37 | +import { AuthModule } from '@bbv/nestjs-auth'; |
| 38 | +import { StorageModule } from '@bbv/nestjs-storage'; |
| 39 | +import { NotificationModule } from '@bbv/nestjs-notifications'; |
| 40 | + |
| 41 | +@Module({ |
| 42 | + imports: [ |
| 43 | + ConfigModule.forRoot({ isGlobal: true }), |
| 44 | + PrismaModule.forRoot({ isGlobal: true }), |
| 45 | + |
| 46 | + AuthModule.forRootAsync({ |
| 47 | + useFactory: (config: ConfigService) => ({ |
| 48 | + jwt: { secret: config.getOrThrow('JWT_SECRET') }, |
| 49 | + features: { |
| 50 | + emailPassword: true, |
| 51 | + google: true, |
| 52 | + organizations: true, |
| 53 | + emailVerification: true, |
| 54 | + passwordReset: true, |
| 55 | + }, |
| 56 | + providers: { |
| 57 | + google: { |
| 58 | + clientId: config.getOrThrow('GOOGLE_CLIENT_ID'), |
| 59 | + clientSecret: config.getOrThrow('GOOGLE_CLIENT_SECRET'), |
| 60 | + callbackUrl: '/auth/google/callback', |
| 61 | + }, |
| 62 | + }, |
| 63 | + }), |
| 64 | + inject: [ConfigService], |
| 65 | + }), |
| 66 | + |
| 67 | + StorageModule.forRootAsync({ |
| 68 | + useFactory: (config: ConfigService) => ({ |
| 69 | + provider: 's3', |
| 70 | + providerOptions: { |
| 71 | + endpoint: config.get('S3_ENDPOINT'), |
| 72 | + accessKeyId: config.getOrThrow('S3_ACCESS_KEY'), |
| 73 | + secretAccessKey: config.getOrThrow('S3_SECRET_KEY'), |
| 74 | + bucket: config.getOrThrow('S3_BUCKET'), |
| 75 | + }, |
| 76 | + features: { trackUploads: true, signedUrls: true }, |
| 77 | + }), |
| 78 | + inject: [ConfigService], |
| 79 | + }), |
| 80 | + |
| 81 | + NotificationModule.forRootAsync({ |
| 82 | + useFactory: (config: ConfigService) => ({ |
| 83 | + channels: { |
| 84 | + email: { |
| 85 | + enabled: true, |
| 86 | + provider: 'smtp', |
| 87 | + providerOptions: { |
| 88 | + host: config.get('SMTP_HOST', 'localhost'), |
| 89 | + port: 587, |
| 90 | + from: 'noreply@app.com', |
| 91 | + }, |
| 92 | + }, |
| 93 | + inApp: { enabled: true }, |
| 94 | + }, |
| 95 | + queue: { redis: { host: config.get('REDIS_HOST', 'localhost') } }, |
| 96 | + }), |
| 97 | + inject: [ConfigService], |
| 98 | + }), |
| 99 | + ], |
| 100 | +}) |
| 101 | +export class AppModule {} |
| 102 | +``` |
| 103 | + |
| 104 | +Then copy plugin Prisma schemas and run migrations: |
| 105 | + |
| 106 | +```bash |
| 107 | +# Copy plugin schemas (one-time, version-control these) |
| 108 | +cp node_modules/@bbv/nestjs-auth/prisma/auth.prisma prisma/schema/ |
| 109 | +cp node_modules/@bbv/nestjs-notifications/prisma/notifications.prisma prisma/schema/ |
| 110 | +cp node_modules/@bbv/nestjs-storage/prisma/storage.prisma prisma/schema/ |
| 111 | + |
| 112 | +# Generate client + migrate |
| 113 | +npx prisma generate |
| 114 | +npx prisma migrate dev |
| 115 | +``` |
| 116 | + |
| 117 | +## Multi-File Prisma Schema |
| 118 | + |
| 119 | +Each Tier 1 plugin ships a `.prisma` file. Your project uses Prisma's native multi-file schema support: |
| 120 | + |
| 121 | +``` |
| 122 | +prisma/ |
| 123 | + schema/ |
| 124 | + base.prisma # datasource + generator (with prismaSchemaFolder) |
| 125 | + auth.prisma # from @bbv/nestjs-auth |
| 126 | + notifications.prisma # from @bbv/nestjs-notifications |
| 127 | + storage.prisma # from @bbv/nestjs-storage |
| 128 | + audit.prisma # from @bbv/nestjs-audit-log |
| 129 | + app.prisma # your project-specific models |
| 130 | +``` |
| 131 | + |
| 132 | +`base.prisma`: |
| 133 | +```prisma |
| 134 | +generator client { |
| 135 | + provider = "prisma-client-js" |
| 136 | + previewFeatures = ["prismaSchemaFolder"] |
| 137 | +} |
| 138 | +
|
| 139 | +datasource db { |
| 140 | + provider = "postgresql" |
| 141 | + url = env("DATABASE_URL") |
| 142 | +} |
| 143 | +``` |
| 144 | + |
| 145 | +## Feature Flags |
| 146 | + |
| 147 | +Every plugin module accepts a `features` config to toggle capabilities: |
| 148 | + |
| 149 | +```typescript |
| 150 | +AuthModule.forRootAsync({ |
| 151 | + useFactory: () => ({ |
| 152 | + jwt: { secret: 'my-secret' }, |
| 153 | + features: { |
| 154 | + emailPassword: true, // POST /auth/register, POST /auth/login |
| 155 | + google: false, // Google OAuth routes not registered |
| 156 | + organizations: true, // Full /organizations CRUD |
| 157 | + sessionManagement: false, // Session endpoints not registered |
| 158 | + }, |
| 159 | + }), |
| 160 | +}) |
| 161 | +``` |
| 162 | + |
| 163 | +When a feature is off: |
| 164 | +- Its routes are **not registered** |
| 165 | +- Its services **throw** if called directly |
| 166 | +- No runtime overhead |
| 167 | + |
| 168 | +## Provider Abstraction |
| 169 | + |
| 170 | +All modules with swappable providers follow the same config pattern: |
| 171 | + |
| 172 | +```typescript |
| 173 | +{ |
| 174 | + provider: 'provider_name', |
| 175 | + providerOptions: { /* typed config for that provider */ }, |
| 176 | +} |
| 177 | +``` |
| 178 | + |
| 179 | +TypeScript discriminated unions ensure type safety per provider. |
| 180 | + |
| 181 | +## Development |
| 182 | + |
| 183 | +```bash |
| 184 | +# Install dependencies |
| 185 | +npm install |
| 186 | + |
| 187 | +# Build all packages |
| 188 | +npm run build |
| 189 | + |
| 190 | +# Run all tests |
| 191 | +npm run test |
| 192 | + |
| 193 | +# Lint all packages |
| 194 | +npm run lint |
| 195 | + |
| 196 | +# Type-check all packages |
| 197 | +npm run typecheck |
| 198 | + |
| 199 | +# Run the demo app |
| 200 | +cd apps/demo |
| 201 | +npm run docker # start Postgres + Redis |
| 202 | +npm run setup # prisma generate + migrate |
| 203 | +npm run dev # http://localhost:3000/api (Swagger) |
| 204 | +``` |
| 205 | + |
| 206 | +## Contributing |
| 207 | + |
| 208 | +1. Fork the repository |
| 209 | +2. Create your feature branch (`git checkout -b feat/my-feature`) |
| 210 | +3. Commit your changes (`git commit -m 'feat(package): add feature'`) |
| 211 | +4. Push to the branch (`git push origin feat/my-feature`) |
| 212 | +5. Open a Pull Request |
| 213 | + |
| 214 | +We use [Changesets](https://github.com/changesets/changesets) for versioning. Add a changeset with `npx changeset` before submitting your PR. |
| 215 | + |
| 216 | +## License |
| 217 | + |
| 218 | +[MIT](./LICENSE) - BlackBox Vision |
0 commit comments