Library / SDK
nestjs/jwt avatar
nestjs/jwt

@nestjs/jwt: jsonwebtoken wrapped in a Nest module

JWT utilities module based on the jsonwebtoken package 🔓

687 stars91 forksTypeScriptMIT

At a glance

What is it?
@nestjs/jwt is the official Nest package for signing and verifying JSON Web Tokens, wrapping the jsonwebtoken library with a JwtModule that can be configured synchronously or asynchronously and a JwtService that adds per-call key overrides. It suits a Nest application that needs token auth without hand-rolling a provider, and it adds nothing to the token format itself.
Who is it for?
@nestjs/jwt fits a Nest application that wants token authentication with configuration coming from a config service, a secret provider or an asymmetric key pair, and it does not fit a project that needs token features the jsonwebtoken library does not have, because this package inherits that surface rather than extending it.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A Nest module over jsonwebtoken, and nothing more

@nestjs/jwt is described in the repository as a JWT utilities module for Nest based on the jsonwebtoken package, and the README says the same thing more directly: JWT utilities module for Nest based on the jsonwebtoken package. The primary language is TypeScript, the licence is MIT, and the last push was on 2026-10-19, with releases 12.0.3 from 2026-10-12, 12.0.2 from 2026-09-14 and 12.0.1 from 2026-08-27.

It is worth being precise about what that means, because the name invites more expectation than the package delivers. The JwtService uses jsonwebtoken underneath, and the sign, verify and decode methods are described as implementations of the corresponding jsonwebtoken methods. The token format is unchanged, the algorithm support is unchanged, and the validation semantics are unchanged. What the package adds is the Nest idiom around it.

That idiom is three things. A JwtModule that can be registered with static options or asynchronously, a JwtService that is injectable and available anywhere in the dependency injection graph, and a documented set of options with a precedence order for the secret and the keys.

The dependency list confirms the shape. The package has two runtime dependencies, jsonwebtoken at 9.0.3 and the @types/jsonwebtoken typings at 9.0.10, and a single peer dependency on @nestjs/common with a range covering versions 8 through 12. The library is small because it does not try to be a token platform; it is a Nest-shaped adapter over a well known Node library, and the version 12 line tracks the Nest 12 release.

For a reader deciding whether to use it, the useful question is therefore not what this package can do, but whether Nest's configuration and injection model is the right shape for how your keys are managed. If it is, the package is a small convenience. If your team already has a token service and a config layer, the underlying library is the same either way.

Registering the module and injecting the service

The usage section is two steps, and there is nothing else to learn before you have a working service.

Installation is an npm install of the scoped package.

bash
$ npm i --save @nestjs/jwt

Registering the module is a decorator on your auth module, with the secret passed in the options object.

typescript
@Module({
  imports: [JwtModule.register({ secret: 'hard!to-guess_secret' })],
  providers: [...],
})
export class AuthModule {}

Then the service is injected like any other provider.

typescript
@Injectable()
export class AuthService {
  constructor(private readonly jwtService: JwtService) {}
}

That is the whole surface for the common case, and it is worth noticing what is absent. There is no token store, no session, no refresh token handling, no revocation list, and no user lookup. The package issues and checks tokens and nothing else, so the pieces an application usually needs around JWT, such as deciding what goes in the payload, when a token expires, and what to do when one is rejected, are all still yours.

The peer dependency range is also worth noting. @nestjs/common is required at ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0, so the package is usable across five major versions of Nest, which is unusual and useful for an application that has not finished its own Nest upgrade. The dev dependencies pin @nestjs/common, @nestjs/core and @nestjs/testing at 12.1.3, so the version 12 line is what is actually tested.

Key management, and the precedence order that decides which key is used

The secret and encryption key options are the part of the package that goes beyond the raw library, and the precedence rules are worth reading carefully because they decide which key is actually used at signing and at verification time.

The static options are secret, publicKey and privateKey. The secret has precedence over the keys, and the public and private keys are used in asymmetric algorithms and are required if no other secrets are present.

On top of that sits secretOrKeyProvider, a function that resolves the key dynamically, and it has precedence over the static secret or the public and private keys. The provider receives the request type, which distinguishes signing from verifying, and the token or payload.

typescript
JwtModule.register({
   /* Secret has precedence over keys */
  secret: 'hard!to-guess_secret',

  /* public key used in asymmetric algorithms (required if non other secrets present) */
  publicKey: '...',

  /* private key used in asymmetric algorithms (required if non other secrets present) */
  privateKey: '...',

  /* Dynamic key provider has precedence over static secret or pub/private keys */
  secretOrKeyProvider: (
    requestType: JwtSecretRequestType,
    tokenOrPayload: string | Object | Buffer,
    verifyOrSignOrOptions?: jwt.VerifyOptions | jwt.SignOptions
  ) => {
    switch (requestType) {
      case JwtSecretRequestType.SIGN:
        // retrieve signing key dynamically
        return 'privateKey';
      case JwtSecretRequestType.VERIFY:
        // retrieve public key for verification dynamically
        return 'publicKey';
      default:
        // retrieve secret dynamically
        return 'hard!to-guess_secret';
    }
  },
});

The example is the shape of a key rotation scheme. A provider that returns the private key when signing and the public key when verifying is exactly what an asymmetric setup needs, and the request type is what tells the two apart.

There is a catch that appears twice in the documentation, once here and once in the API spec, and it is the sort of thing that surfaces as a confusing runtime error. If you want to control secret and key management dynamically you can use the secretOrKeyProvider function, and you can also use an asynchronous version of it. But for the asynchronous version of secretOrKeyProvider, the synchronous versions of .sign() and .verify() will throw an exception.

So the choice of a provider determines which methods you can call. A synchronous provider means sign and verify work. An asynchronous one means you must use signAsync and verifyAsync, which do exist in the API, and calling the synchronous form will throw.

registerAsync, and the three ways to supply options that are not ready yet

The async options section addresses a common situation: quite often you might want to asynchronously pass your module options instead of passing them beforehand. The registerAsync method provides a couple of various ways to deal with async data, and the README documents three.

The first uses a factory.

typescript
JwtModule.registerAsync({
  useFactory: () => ({
    secret: 'hard!to-guess_secret'
  })
});

As the README notes, the factory behaves like every other one, so it might be async and is able to inject dependencies through inject. The second example does exactly that, pulling the secret out of a ConfigService.

typescript
JwtModule.registerAsync({
  imports: [ConfigModule],
  useFactory: async (configService: ConfigService) => ({
    secret: configService.get<string>('SECRET'),
  }),
  inject: [ConfigService],
}),

The second way uses a class, which the README says will instantiate JwtConfigService inside JwtModule and will use it to create the options object.

typescript
JwtModule.registerAsync({
  useClass: JwtConfigService
});

The class has to implement the factory interface.

typescript
class JwtConfigService implements JwtOptionsFactory {
  createJwtOptions(): JwtModuleOptions {
    return {
      secret: 'hard!to-guess_secret'
    };
  }
}

The third way uses an existing provider.

typescript
JwtModule.registerAsync({
  imports: [ConfigModule],
  useExisting: ConfigService,
}),

And here the README draws the distinction precisely. It works the same as useClass with one critical difference, that JwtModule will look up imported modules to reuse an already created ConfigService, instead of instantiating it on its own.

That distinction is the whole reason to pick one over the other. useClass creates a second instance of the configuration class inside the JWT module, which is usually harmless but duplicates any state it holds. useExisting reuses the instance your application already has, which matters if the config service caches or if it is instrumented.

Per-call key overrides, and the two ways signing can fail

The API spec section lists the service methods, and one capability stands out because jsonwebtoken does not have it.

The sign method takes a payload and optional sign options, and it is an implementation of jsonwebtoken's sign. Differing from jsonwebtoken, it also allows an additional secret, privateKey and publicKey properties on options to override options passed in from the module. It only overrides the secret, publicKey or privateKey though not a secretOrKeyProvider.

The verify method behaves the same way, and the override does not extend to the provider. So the per-call override is a way to say use this particular key for this one call, and the provider remains the module-level authority.

The asynchronous variants exist for both. signAsync returns a Promise of a string, and verifyAsync returns a Promise of the decoded payload, and both are the asynchronous versions of the synchronous methods. There is also a decode method, which is an implementation of jsonwebtoken's decode, with an overload that returns the complete decoded structure including the payload when the complete option is set.

The failure modes are documented rather than left to be discovered. Both sign and verify carry the note that they will throw an exception for the asynchronous version of secretOrKeyProvider. And the reason to read that note is that it is a runtime error rather than a type error, so a project that starts with a synchronous provider and later swaps in an asynchronous one can pass its type checks and then fail on the first login.

The type story is otherwise conventional. verify is generic over the payload type, with a default of any, so a controller can declare the shape it expects and get it back typed, and decode is generic in the same way.

Editorial conclusion

@nestjs/jwt fits a Nest application that wants token authentication with configuration coming from a config service, a secret provider or an asymmetric key pair, and it does not fit a project that needs token features the jsonwebtoken library does not have, because this package inherits that surface rather than extending it. Before wiring it up, decide how the signing key is going to be supplied, because the asynchronous secretOrKeyProvider form makes the synchronous sign and verify methods throw an exception, and pick the algorithm deliberately rather than accepting the default symmetric secret.

Frequently asked questions

How do I install and set up @nestjs/jwt?

Run npm i --save @nestjs/jwt, then register the module with JwtModule.register({ secret: '...' }) in your auth module's imports, and inject JwtService into the service that needs to sign or verify tokens.

What is the difference between secret, publicKey and secretOrKeyProvider in @nestjs/jwt?

The secret has precedence over the public and private keys, and the dynamic secretOrKeyProvider has precedence over the static secret or keys. The provider receives a request type so you can return the signing key when signing and the verification key when verifying.

What happens if I use an async secretOrKeyProvider with sign or verify?

The synchronous versions of sign and verify will throw an exception. The README notes this in both the key options section and the API spec, and the asynchronous methods signAsync and verifyAsync are provided for that case.

How do I supply JWT options from a config service in @nestjs/jwt?

Use JwtModule.registerAsync with useFactory and inject, importing ConfigModule so the factory can take a ConfigService as a dependency. The README also documents useClass, which instantiates the factory class inside JwtModule, and useExisting, which reuses an already created instance from the imported modules instead of instantiating its own.

Official sources

  1. License: MIT
  2. nestjs/jwt on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/nestjs-jwt.svg)](https://hysenlabs.com/projects/nestjs-jwt)

Was this useful?