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

@bilikaz/nestjs-starter-boilerplate

v1.0.0

Published

Minimal NestJS boilerplate with TypeORM, JWT/OpenID authentication, and OpenAPI (Swagger) documentation out of the box

Readme

NestJS Starter

Minimal NestJS boilerplate with TypeORM, JWT/OpenID authentication, and OpenAPI (Swagger) documentation out of the box.

Stack

  • NestJS v11 — modular, decorator-based Node.js framework
  • TypeORM 0.3 — ORM with MySQL driver, snake_case naming strategy, migration support
  • Authentication — JWT access/refresh tokens, local (username+password) strategy via Passport; roles-based access control (@Roles, @Public, @OrganizationRoles decorators) with composable OR-guard support via AnyGuard
  • OpenAPI — Swagger UI auto-generated from decorators, available at /api

Getting started

npm install
cp demo.env .env   # fill in real values
npm run start:dev

Environment variables

Copy demo.env to .env and adjust the values.

| Variable | Description | Default | |---|---|---| | APP_PORT | HTTP port the server listens on | 3000 | | APP_TITLE | Swagger UI title | Example | | APP_DESCRIPTION | Swagger UI description | description | | APP_VERSION | Swagger UI version | 1.0 | | DATABASE_HOST | MySQL host | — | | DATABASE_PORT | MySQL port | — | | DATABASE_USER | MySQL username | — | | DATABASE_PASSWORD | MySQL password | — | | DATABASE_NAME | MySQL database name | — | | JWT_SECRET | Secret used to sign JWT tokens | — | | JWT_EXPIRATION | Access token lifetime in seconds | 900 | | JWT_REFRESH_EXPIRATION | Refresh token lifetime in seconds | 604800 | | OIDC_ISSUER | JWT iss claim value | — | | OIDC_AUDIENCE | JWT aud claim value | api |

Scripts

npm run start:dev       # development with watch mode
npm run start:debug     # debug mode with Node inspector
npm run build           # compile TypeScript to dist/
npm run start:prod      # run compiled build

npm test                # unit tests
npm run test:watch      # unit tests in watch mode
npm run test:cov        # unit tests with coverage report
npm run test:e2e        # end-to-end tests

npm run lint            # ESLint with auto-fix
npm run format          # Prettier formatting

npm run seed:create -- -n db/seeds/DescriptiveName  # create a new seed file
npm run seed:run        # run all seeds

npm run migration:generate -- db/migrations/DescriptiveName  # generate migration from entity changes
npm run migration:run    # apply pending migrations
npm run migration:revert # revert last migration
npm run migration:show   # show migration status

Database migrations

Migrations live in db/migrations/. The datasource is configured in db/datasource.ts. Auto-synchronize is disabled — all schema changes must go through migrations.

# Generate a new migration from entity changes
npm run migration:generate -- db/migrations/DescriptiveName

# Apply all pending migrations
npm run migration:run

# Revert the last applied migration
npm run migration:revert

# Show migration status (applied / pending)
npm run migration:show

The migration commands use typeorm-ts-node-commonjs and load db/datasource.ts directly, so they pick up the .env file automatically.

Database seeding

Seeding uses typeorm-extension. Seed files live in db/seeds/. Whether a seed is idempotent depends on its track attribute — seeds with track: true are only run once and skipped on subsequent runs.

# Create a new seed file
npm run seed:create -- -n db/seeds/DescriptiveName

# Run all seeds
npm run seed:run

The initial seed creates an admin user:

| Field | Value | |---|---| | username | admin | | email | admin@localhost | | password | admin | | role | admin |

Claude Code

This project includes a Claude Code skill for scaffolding new CRUD modules. With Claude Code CLI installed, run:

/createEntity <EntityName> <field:type[:options]> ...

This generates a complete module — entity, DTOs, repository, service, controller, and module file — and wires it into AppModule. All controller actions are protected with @Roles(UserRole.ADMIN) by default. Use role flags to override per-action:

# All actions require ADMIN role (default)
/createEntity Product name:string price:decimal

# Custom roles per action
/createEntity Product name:string --create-roles=ADMIN --list-roles=USER,ADMIN --get-public

# All actions public (no auth required)
/createEntity Product name:string --no-roles

See .claude/skills/createEntity/SKILL.md for the full field syntax and all available role flags.

To scaffold a new guard that integrates with the AnyGuard OR-composition system, use:

/createGuard <GuardName> <description of what it restricts>

This generates the decorator and guard class (with anyGuard protocol wired in), registers it in AuthModule, and appends it to the global AnyGuard(...) call in AppModule. Pass --no-any-guard to skip the last step.

# Guard that checks a user's subscription tier
/createGuard SubscriptionTier restricts routes to users with a minimum subscription tier

# Same, but don't add it to the global AnyGuard automatically
/createGuard SubscriptionTier restricts routes to users with a minimum subscription tier --no-any-guard

See .claude/skills/createGuard/SKILL.md for full details and the AnyGuard protocol contract.

Authorization

Global role guard — @Roles

All routes are protected by default. Use @Roles(UserRole.X) to restrict a route to specific system-level roles, or @Public() to opt out entirely.

@Roles(UserRole.ADMIN)   // only admins
@Roles(UserRole.USER, UserRole.ADMIN)  // either role
@Public()                // no auth required

Organization-scoped guard — @OrganizationRoles

OrganizationRoleGuard enforces membership and optional role within an organization. It reads organizationId from the route params (configurable via the decorator's param option).

@OrganizationRoles()                             // any member
@OrganizationRoles(OrganizationUserRole.OWNER)   // owner only
@OrganizationRoles(OrganizationUserRole.OWNER, OrganizationUserRole.MANAGER)

// Custom param name (default is 'organizationId')
@OrganizationRoles(OrganizationUserRole.OWNER, { param: 'orgId' })

Composable OR logic — AnyGuard

By default NestJS applies guards with AND logic — all must pass. AnyGuard is a factory that wraps multiple guards and passes if any one of them allows the request (OR logic). The globally registered instance combines RoleGuard and OrganizationRoleGuard:

// app.module.ts
{ provide: APP_GUARD, useClass: AnyGuard(RoleGuard, OrganizationRoleGuard) }

This means a route annotated with both decorators is accessible to system admins or organization owners — whichever check passes first:

@Roles(UserRole.ADMIN)
@OrganizationRoles(OrganizationUserRole.OWNER)
@Get(':organizationId/settings')
getSettings() { ... }

How guards signal their result to AnyGuard — each compatible guard exposes an anyGuard: boolean property:

| anyGuard value | No decorator on route | Check passes | Check fails | |---|---|---|---| | false (standalone default) | true — transparent | true | false | | true (set by AnyGuard) | false — not applicable | true | throws ForbiddenException |

AnyGuard resolves as follows:

  • Any guard returns trueallowed
  • All guards returned false (none applicable) → allowed (route has no restriction)
  • At least one threw and none returned trueforbidden

Adding a custom guard compatible with AnyGuard

Any guard can participate in OR composition by following the same contract as OrganizationRoleGuard:

  1. Add a public anyGuard = false property.
  2. When anyGuard is true: return false if your decorator is absent, throw ForbiddenException if the check fails.
  3. Pass the class to AnyGuard(...) in app.module.ts.

Swagger / OpenAPI

Once the app is running, the interactive API documentation is available at:

http://localhost:<APP_PORT>/api

All endpoints are documented with request/response schemas. Protected endpoints require a Bearer token — click Authorize in the Swagger UI and paste a JWT access token obtained from POST /auth/login.