diff --git a/LICENSE b/LICENSE index bdf11c44a1..ad0b9f46a4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ (The MIT License) -Copyright (c) 2017-2023 Kamil Myśliwiec +Copyright (c) 2017-2025 Kamil Myśliwiec Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/content/cli/workspaces.md b/content/cli/workspaces.md index 06ad6e9242..930dacba97 100644 --- a/content/cli/workspaces.md +++ b/content/cli/workspaces.md @@ -3,7 +3,7 @@ Nest has two modes for organizing code: - **standard mode**: useful for building individual project-focused applications that have their own dependencies and settings, and don't need to optimize for sharing modules, or optimizing complex builds. This is the default mode. -- **monorepo mode**: this mode treats code artifacts as part of a lightweight **monorepo**, and may be more appropriate for teams of developers and/or multi-project environments. It automates parts of the build process to make it easy to create and compose modular components, promotes code re-use, makes integration testing easier, makes it easy to share project-wide artifacts like `eslint` rules and other configuration policies, and is easier to use than alternatives like github submodules. Monorepo mode employs the concept of a **workspace**, represented in the `nest-cli.json` file, to coordinate the relationship between the components of the monorepo. +- **monorepo mode**: this mode treats code artifacts as part of a lightweight **monorepo**, and may be more appropriate for teams of developers and/or multi-project environments. It automates parts of the build process to make it easy to create and compose modular components, promotes code re-use, makes integration testing easier, makes it easy to share project-wide artifacts like `eslint` rules and other configuration policies, and is easier to use than alternatives like Git submodules. Monorepo mode employs the concept of a **workspace**, represented in the `nest-cli.json` file, to coordinate the relationship between the components of the monorepo. It's important to note that virtually all of Nest's features are independent of your code organization mode. The **only** effect of this choice is how your projects are composed and how build artifacts are generated. All other functionality, from the CLI to core modules to add-on modules work the same in either mode. diff --git a/content/controllers.md b/content/controllers.md index c7a0583e29..36a25c1f7b 100644 --- a/content/controllers.md +++ b/content/controllers.md @@ -319,7 +319,7 @@ export class AdminController { > warning **Warning** Since **Fastify** does not support nested routers, if you are using sub-domain routing, it is recommended to use the default Express adapter instead. -Similar to a route `path`, the `hosts` option can use tokens to capture the dynamic value at that position in the host name. The host parameter token in the `@Controller()` decorator example below demonstrates this usage. Host parameters declared in this way can be accessed using the `@HostParam()` decorator, which should be added to the method signature. +Similar to a route `path`, the `host` option can use tokens to capture the dynamic value at that position in the host name. The host parameter token in the `@Controller()` decorator example below demonstrates this usage. Host parameters declared in this way can be accessed using the `@HostParam()` decorator, which should be added to the method signature. ```typescript @Controller({ host: ':account.example.com' }) diff --git a/content/fundamentals/circular-dependency.md b/content/fundamentals/circular-dependency.md index 3c0da68418..c0c133b8ab 100644 --- a/content/fundamentals/circular-dependency.md +++ b/content/fundamentals/circular-dependency.md @@ -6,7 +6,7 @@ While circular dependencies should be avoided where possible, you can't always d We also describe resolving circular dependencies between modules. -> warning **Warning** A circular dependency might also be caused when using "barrel files"/index.ts files to group imports. Barrel files should be omitted when it comes to module/provider classes. For example, barrel files should not be used when importing files within the same directory as the barrel file, i.e. `cats/cats.controller` should not import `cats` to import the `cats/cats.service` file. For more details please also see [this github issue](https://github.com/nestjs/nest/issues/1181#issuecomment-430197191). +> warning **Warning** A circular dependency might also be caused when using "barrel files"/index.ts files to group imports. Barrel files should be omitted when it comes to module/provider classes. For example, barrel files should not be used when importing files within the same directory as the barrel file, i.e. `cats/cats.controller` should not import `cats` to import the `cats/cats.service` file. For more details please also see [this GitHub issue](https://github.com/nestjs/nest/issues/1181#issuecomment-430197191). #### Forward reference diff --git a/content/fundamentals/dynamic-modules.md b/content/fundamentals/dynamic-modules.md index f1a3a8731e..b98490e0e1 100644 --- a/content/fundamentals/dynamic-modules.md +++ b/content/fundamentals/dynamic-modules.md @@ -343,6 +343,45 @@ Extending the `ConfigurableModuleClass` means that `ConfigModule` provides now n export class AppModule {} ``` +The `registerAsync` method takes the following object as an argument: + +```typescript +{ + /** + * Injection token resolving to a class that will be instantiated as a provider. + * The class must implement the corresponding interface. + */ + useClass?: Type< + ConfigurableModuleOptionsFactory + >; + /** + * Function returning options (or a Promise resolving to options) to configure the + * module. + */ + useFactory?: (...args: any[]) => Promise | ModuleOptions; + /** + * Dependencies that a Factory may inject. + */ + inject?: FactoryProvider['inject']; + /** + * Injection token resolving to an existing provider. The provider must implement + * the corresponding interface. + */ + useExisting?: Type< + ConfigurableModuleOptionsFactory + >; +} +``` + +Let's go through the above properties one by one: + +- `useFactory` - a function that returns the configuration object. It can be either synchronous or asynchronous. To inject dependencies into the factory function, use the `inject` property. We used this variant in the example above. +- `inject` - an array of dependencies that will be injected into the factory function. The order of the dependencies must match the order of the parameters in the factory function. +- `useClass` - a class that will be instantiated as a provider. The class must implement the corresponding interface. Typically, this is a class that provides a `create()` method that returns the configuration object. Read more about this in the [Custom method key](/fundamentals/dynamic-modules#custom-method-key) section below. +- `useExisting` - a variant of `useClass` that allows you to use an existing provider instead of instructing Nest to create a new instance of the class. This is useful when you want to use a provider that is already registered in the module. Keep in mind that the class must implement the same interface as the one used in `useClass` (and so it must provide the `create()` method, unless you override the default method name, see [Custom method key](/fundamentals/dynamic-modules#custom-method-key) section below). + +Always choose one of the above options (`useFactory`, `useClass`, or `useExisting`), as they are mutually exclusive. + Lastly, let's update the `ConfigService` class to inject the generated module options' provider instead of the `'CONFIG_OPTIONS'` that we used so far. ```typescript @@ -431,17 +470,18 @@ There are edge-cases when your module may need to take extra options that determ In such cases, the `ConfigurableModuleBuilder#setExtras` method can be used. See the following example: ```typescript -export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } = new ConfigurableModuleBuilder() - .setExtras( - { - isGlobal: true, - }, - (definition, extras) => ({ - ...definition, - global: extras.isGlobal, - }), - ) - .build(); +export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } = + new ConfigurableModuleBuilder() + .setExtras( + { + isGlobal: true, + }, + (definition, extras) => ({ + ...definition, + global: extras.isGlobal, + }), + ) + .build(); ``` In the example above, the first argument passed into the `setExtras` method is an object containing default values for the "extra" properties. The second argument is a function that takes an auto-generated module definitions (with `provider`, `exports`, etc.) and `extras` object which represents extra properties (either specified by the consumer or defaults). The returned value of this function is a modified module definition. In this specific example, we're taking the `extras.isGlobal` property and assigning it to the `global` property of the module definition (which in turn determines whether a module is global or not, read more [here](/modules#dynamic-modules)). @@ -465,7 +505,9 @@ However, since `isGlobal` is declared as an "extra" property, it won't be availa ```typescript @Injectable() export class ConfigService { - constructor(@Inject(MODULE_OPTIONS_TOKEN) private options: ConfigModuleOptions) { + constructor( + @Inject(MODULE_OPTIONS_TOKEN) private options: ConfigModuleOptions, + ) { // "options" object will not have the "isGlobal" property // ... } @@ -479,7 +521,11 @@ The auto-generated static methods (`register`, `registerAsync`, etc.) can be ext ```typescript import { Module } from '@nestjs/common'; import { ConfigService } from './config.service'; -import { ConfigurableModuleClass, ASYNC_OPTIONS_TYPE, OPTIONS_TYPE } from './config.module-definition'; +import { + ConfigurableModuleClass, + ASYNC_OPTIONS_TYPE, + OPTIONS_TYPE, +} from './config.module-definition'; @Module({ providers: [ConfigService], @@ -505,5 +551,10 @@ export class ConfigModule extends ConfigurableModuleClass { Note the use of `OPTIONS_TYPE` and `ASYNC_OPTIONS_TYPE` types that must be exported from the module definition file: ```typescript -export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN, OPTIONS_TYPE, ASYNC_OPTIONS_TYPE } = new ConfigurableModuleBuilder().build(); +export const { + ConfigurableModuleClass, + MODULE_OPTIONS_TOKEN, + OPTIONS_TYPE, + ASYNC_OPTIONS_TYPE, +} = new ConfigurableModuleBuilder().build(); ``` diff --git a/content/fundamentals/unit-testing.md b/content/fundamentals/unit-testing.md index f6096c6aab..1721ee3cd6 100644 --- a/content/fundamentals/unit-testing.md +++ b/content/fundamentals/unit-testing.md @@ -166,7 +166,7 @@ Nest also allows you to define a mock factory to apply to all of your missing de ```typescript // ... -import { ModuleMocker, MockFunctionMetadata } from 'jest-mock'; +import { ModuleMocker, MockMetadata } from 'jest-mock'; const moduleMocker = new ModuleMocker(global); @@ -185,8 +185,10 @@ describe('CatsController', () => { if (typeof token === 'function') { const mockMetadata = moduleMocker.getMetadata( token, - ) as MockFunctionMetadata; - const Mock = moduleMocker.generateFromMetadata(mockMetadata); + ) as MockMetadata; + const Mock = moduleMocker.generateFromMetadata( + mockMetadata, + ) as ObjectConstructor; return new Mock(); } }) diff --git a/content/graphql/quick-start.md b/content/graphql/quick-start.md index f68f65c997..749890b2b0 100644 --- a/content/graphql/quick-start.md +++ b/content/graphql/quick-start.md @@ -12,10 +12,10 @@ Start by installing the required packages: ```bash # For Express and Apollo (default) -$ npm i @nestjs/graphql @nestjs/apollo @apollo/server graphql +$ npm i @nestjs/graphql @nestjs/apollo @apollo/server@^4.12.2 graphql # For Fastify and Apollo -# npm i @nestjs/graphql @nestjs/apollo @apollo/server @as-integrations/fastify graphql +# npm i @nestjs/graphql @nestjs/apollo @apollo/server@^4.12.2 @as-integrations/fastify graphql # For Fastify and Mercurius # npm i @nestjs/graphql @nestjs/mercurius graphql mercurius @@ -88,19 +88,18 @@ With that in place, and with your application running in the background, you can -> warning **Note** `@nestjs/mercurius` integration does not ship with the built-in GraphQL Playground integration. Instead, you can use [GraphiQL](https://github.com/graphql/graphiql) (set `graphiql: true`). +> info **Note** `@nestjs/mercurius` integration does not ship with the built-in GraphQL Playground integration. Instead, you can use [GraphiQL](https://github.com/graphql/graphiql) (set `graphiql: true`). -#### Multiple endpoints - -Another useful feature of the `@nestjs/graphql` module is the ability to serve multiple endpoints at once. This lets you decide which modules should be included in which endpoint. By default, `GraphQL` searches for resolvers throughout the whole app. To limit this scan to only a subset of modules, use the `include` property. - -```typescript -GraphQLModule.forRoot({ - include: [CatsModule], -}), -``` - -> warning **Warning** If you use the `@apollo/server` with `@as-integrations/fastify` package with multiple GraphQL endpoints in a single application, make sure to enable the `disableHealthCheck` setting in the `GraphQLModule` configuration. +> warning **Warning** Update (04/14/2025): The default Apollo playground has been deprecated and will be removed in the next major release. Instead, you can use [GraphiQL](https://github.com/graphql/graphiql), just set `graphiql: true` in the `GraphQLModule` configuration, as shown below: +> +> ```typescript +> GraphQLModule.forRoot({ +> driver: ApolloDriver, +> graphiql: true, +> }), +> ``` +> +> If your application uses [subscriptions](/graphql/subscriptions), be sure to use `graphql-ws`, as `subscriptions-transport-ws` isn't supported by GraphiQL. #### Code first @@ -356,6 +355,18 @@ export class AppModule {} The `forRoot()` method takes an options object as an argument. These options are passed through to the underlying driver instance. Read more about available settings [here](https://github.com/mercurius-js/mercurius/blob/master/docs/api/options.md#plugin-options). +#### Multiple endpoints + +Another useful feature of the `@nestjs/graphql` module is the ability to serve multiple endpoints at once. This lets you decide which modules should be included in which endpoint. By default, `GraphQL` searches for resolvers throughout the whole app. To limit this scan to only a subset of modules, use the `include` property. + +```typescript +GraphQLModule.forRoot({ + include: [CatsModule], +}), +``` + +> warning **Warning** If you use the `@apollo/server` with `@as-integrations/fastify` package with multiple GraphQL endpoints in a single application, make sure to enable the `disableHealthCheck` setting in the `GraphQLModule` configuration. + #### Third-party integrations - [GraphQL Yoga](https://github.com/dotansimha/graphql-yoga) diff --git a/content/microservices/basics.md b/content/microservices/basics.md index 7d54fb4eb1..0de4f056df 100644 --- a/content/microservices/basics.md +++ b/content/microservices/basics.md @@ -87,7 +87,7 @@ The second argument of the `createMicroservice()` method is an `options` object. serializer - Custom serializer for outcoming messages + Custom serializer for outgoing messages deserializer diff --git a/content/microservices/custom-transport.md b/content/microservices/custom-transport.md index 4ec054b30a..8ca6c847da 100644 --- a/content/microservices/custom-transport.md +++ b/content/microservices/custom-transport.md @@ -192,7 +192,13 @@ class GoogleCloudPubSubClient extends ClientProxy { // In a real-world application, the "callback" function should be executed // with payload sent back from the responder. Here, we'll simply simulate (5 seconds delay) // that response came through by passing the same "data" as we've originally passed in. - setTimeout(() => callback({ response: packet.data }), 5000); + // + // The "isDisposed" bool on the WritePacket tells the response that no further data is + // expected. If not sent or is false, this will simply emit data to the Observable. + setTimeout(() => callback({ + response: packet.data, + isDisposed: true, + }), 5000); return () => console.log('teardown'); } diff --git a/content/microservices/kafka.md b/content/microservices/kafka.md index f6ab7a9dce..2660093a67 100644 --- a/content/microservices/kafka.md +++ b/content/microservices/kafka.md @@ -171,7 +171,7 @@ client: ClientKafkaProxy; #### Message pattern -The Kafka microservice message pattern utilizes two topics for the request and reply channels. The `ClientKafkaProxy#send()` method sends messages with a [return address](https://www.enterpriseintegrationpatterns.com/patterns/messaging/ReturnAddress.html) by associating a [correlation id](https://www.enterpriseintegrationpatterns.com/patterns/messaging/CorrelationIdentifier.html), reply topic, and reply partition with the request message. This requires the `ClientKafkaProxy` instance to be subscribed to the reply topic and assigned to at least one partition before sending a message. +The Kafka microservice message pattern utilizes two topics for the request and reply channels. The `ClientKafkaProxy.send()` method sends messages with a [return address](https://www.enterpriseintegrationpatterns.com/patterns/messaging/ReturnAddress.html) by associating a [correlation id](https://www.enterpriseintegrationpatterns.com/patterns/messaging/CorrelationIdentifier.html), reply topic, and reply partition with the request message. This requires the `ClientKafkaProxy` instance to be subscribed to the reply topic and assigned to at least one partition before sending a message. Subsequently, you need to have at least one reply topic partition for every Nest application running. For example, if you are running 4 Nest applications but the reply topic only has 3 partitions, then 1 of the Nest applications will error out when trying to send a message. @@ -183,7 +183,7 @@ To prevent the `ClientKafkaProxy` consumers from losing response messages, a Nes #### Message response subscription -> warning **Note** This section is only relevant if you use [request-response](/microservices/basics#request-response) message style (with the `@MessagePattern` decorator and the `ClientKafkaProxy#send` method). Subscribing to the response topic is not necessary for the [event-based](/microservices/basics#event-based) communication (`@EventPattern` decorator and `ClientKafkaProxy#emit` method). +> warning **Note** This section is only relevant if you use [request-response](/microservices/basics#request-response) message style (with the `@MessagePattern` decorator and the `ClientKafkaProxy.send` method). Subscribing to the response topic is not necessary for the [event-based](/microservices/basics#event-based) communication (`@EventPattern` decorator and `ClientKafkaProxy.emit` method). The `ClientKafkaProxy` class provides the `subscribeToResponseOf()` method. The `subscribeToResponseOf()` method takes a request's topic name as an argument and adds the derived reply topic name to a collection of reply topics. This method is required when implementing the message pattern. @@ -617,7 +617,7 @@ server.status.subscribe((status: KafkaStatus) => { #### Underlying producer and consumer -For more advanced use cases, you may need to access the underlying prodocuer and consumer instances. This can be useful for scenarios like manually closing the connection or using driver-specific methods. However, keep in mind that for most cases, you **shouldn't need** to access the driver directly. +For more advanced use cases, you may need to access the underlying producer and consumer instances. This can be useful for scenarios like manually closing the connection or using driver-specific methods. However, keep in mind that for most cases, you **shouldn't need** to access the driver directly. To do so, you can use `producer` and `consumer` getters exposed by the `ClientKafkaProxy` instance. diff --git a/content/microservices/rabbitmq.md b/content/microservices/rabbitmq.md index e88b722c36..a95e15a9ac 100644 --- a/content/microservices/rabbitmq.md +++ b/content/microservices/rabbitmq.md @@ -48,7 +48,7 @@ The `options` property is specific to the chosen transporter. The Rabbit - + @@ -68,7 +68,7 @@ The `options` property is specific to the chosen transporter. The Rabbit - + @@ -82,6 +82,38 @@ The `options` property is specific to the chosen transporter. The Rabbit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
urlsConnection urlsAn array of connection URLs to try in order
queue
consumerTagConsumer Tag Identifier (read more here)A name which the server will use to distinguish message deliveries for the consumer; mustn’t be already in use on the channel. It’s usually easier to omit this, in which case the server will create a random name and supply it in the reply. Consumer Tag Identifier (read more here)
queueOptionsheaders Headers to be sent along with every message
replyQueueReply queue for the producer. Default is amq.rabbitmq.reply-to
persistentIf truthy, the message will survive broker restarts provided it’s in a queue that also survives restarts
noAssertWhen false, a queue will not be asserted before consuming
wildcardsSet to true only if you want to use Topic Exchange for routing messages to queues. Enabling this will allow you to use wildcards (*, #) as message and event patterns
exchangeName for the exchange. Defaults to the queue name when "wildcards" is set to true
exchangeTypeType of the exchange. Default is topic. Valid values are direct, fanout, topic, and headers
routingKeyAdditional routing key for the topic exchange
maxConnectionAttemptsMaximum number of connection attempts. Applies only to the consumer configuration. -1 === infinite
#### Client @@ -300,3 +332,46 @@ Similarly, you can access the server's underlying driver instance: const managerRef = server.unwrap(); ``` + +#### Wildcards + +RabbitMQ supports the use of wildcards in routing keys to allow for flexible message routing. The `#` wildcard matches zero or more words, while the `*` wildcard matches exactly one word. + +For example, the routing key `cats.#` matches `cats`, `cats.meow`, and `cats.meow.purr`. The routing key `cats.*` matches `cats.meow` but not `cats.meow.purr`. + +To enable wildcard support in your RabbitMQ microservice, set the `wildcards` configuration option to `true` in the options object: + +```typescript +const app = await NestFactory.createMicroservice( + AppModule, + { + transport: Transport.RMQ, + options: { + urls: ['amqp://localhost:5672'], + queue: 'cats_queue', + wildcards: true, + }, + }, +); +``` + +With this configuration, you can use wildcards in your routing keys when subscribing to events/messages. For example, to listen for messages with the routing key `cats.#`, you can use the following code: + +```typescript +@MessagePattern('cats.#') +getCats(@Payload() data: { message: string }, @Ctx() context: RmqContext) { + console.log(`Received message with routing key: ${context.getPattern()}`); + + return { + message: 'Hello from the cats service!', + } +} +``` + +To send a message with a specific routing key, you can use the `send()` method of the `ClientProxy` instance: + +```typescript +this.client.send('cats.meow', { message: 'Meow!' }).subscribe((response) => { + console.log(response); +}); +``` diff --git a/content/migration.md b/content/migration.md index fb187216e1..fac6422dbb 100644 --- a/content/migration.md +++ b/content/migration.md @@ -178,7 +178,7 @@ While the `OnModuleDestroy` hooks are executed in the reverse order: A -> B -> C ``` -> info **Hint** Global modules are treated as if they depend on all other modules. This means that global modules are initialized first and destroyed last. +> info **Hint** Global modules are treated as a dependency of all other modules. This means that global modules are initialized first and destroyed last. #### Middleware registration order diff --git a/content/openapi/cli-plugin.md b/content/openapi/cli-plugin.md index 4af8e59adf..0d90669832 100644 --- a/content/openapi/cli-plugin.md +++ b/content/openapi/cli-plugin.md @@ -173,6 +173,7 @@ export interface PluginOptions { controllerKeyOfComment?: string; introspectComments?: boolean; skipAutoHttpCode?: boolean; + esmCompatible?: boolean; } ``` @@ -217,6 +218,11 @@ export interface PluginOptions { false Disables the automatic addition of @HttpCode() in controllers + + esmCompatible + false + If set to true, resolves syntax errors encountered when using ESM ({ "type": "module" }). + Make sure to delete the `/dist` folder and rebuild your application whenever plugin options are updated. diff --git a/content/openapi/introduction.md b/content/openapi/introduction.md index 9f901779fe..ac6d1ce992 100644 --- a/content/openapi/introduction.md +++ b/content/openapi/introduction.md @@ -37,7 +37,7 @@ async function bootstrap() { bootstrap(); ``` -> info **Hint** The factory method `SwaggerModule#createDocument()` is used specifically to generate the Swagger document when you request it. This approach helps save some initialization time, and the resulting document is a serializable object that conforms to the [OpenAPI Document](https://swagger.io/specification/#openapi-document) specification. Instead of serving the document over HTTP, you can also save it as a JSON or YAML file and use it in various ways. +> info **Hint** The factory method `SwaggerModule.createDocument()` is used specifically to generate the Swagger document when you request it. This approach helps save some initialization time, and the resulting document is a serializable object that conforms to the [OpenAPI Document](https://swagger.io/specification/#openapi-document) specification. Instead of serving the document over HTTP, you can also save it as a JSON or YAML file and use it in various ways. The `DocumentBuilder` helps to structure a base document that conforms to the OpenAPI Specification. It provides several methods that allow setting such properties as title, description, version, etc. In order to create a full document (with all HTTP routes defined) we use the `createDocument()` method of the `SwaggerModule` class. This method takes two arguments, an application instance and a Swagger options object. Alternatively, we can provide a third argument, which should be of type `SwaggerDocumentOptions`. More on this in the [Document options section](/openapi/introduction#document-options). @@ -286,20 +286,21 @@ export interface SwaggerCustomOptions { urls?: Record<'url' | 'name', string>[]; } ``` -> info **Hint** `ui` and `raw` are independent options. Disabling Swagger UI (`ui: false`) does not disable API definitions (JSON/YAML). Conversely, disabling API definitions (`raw: []`) does not disable the Swagger UI. + +> info **Hint** `ui` and `raw` are independent options. Disabling Swagger UI (`ui: false`) does not disable API definitions (JSON/YAML). Conversely, disabling API definitions (`raw: []`) does not disable the Swagger UI. > > For example, the following configuration will disable the Swagger UI but still allow access to API definitions: +> > ```typescript ->const options: SwaggerCustomOptions = { -> ui: false, // Swagger UI is disabled -> raw: ['json'], // JSON API definition is still accessible (YAML is disabled) ->}; ->SwaggerModule.setup('api', app, options); +> const options: SwaggerCustomOptions = { +> ui: false, // Swagger UI is disabled +> raw: ['json'], // JSON API definition is still accessible (YAML is disabled) +> }; +> SwaggerModule.setup('api', app, options); > ``` > > In this case, http://localhost:3000/api-json will still be accessible, but http://localhost:3000/api (Swagger UI) will not. - #### Example A working example is available [here](https://github.com/nestjs/nest/tree/master/sample/11-swagger). diff --git a/content/openapi/operations.md b/content/openapi/operations.md index 60b618f228..0b2a147c1e 100644 --- a/content/openapi/operations.md +++ b/content/openapi/operations.md @@ -138,7 +138,7 @@ You can enable file upload for a specific method with the `@ApiBody` decorator t description: 'List of cats', type: FileUploadDto, }) -uploadFile(@UploadedFile() file) {} +uploadFile(@UploadedFile() file: Express.Multer.File) {} ``` Where `FileUploadDto` is defined as follows: diff --git a/content/openapi/types-and-parameters.md b/content/openapi/types-and-parameters.md index fdb7e1a70d..5c881e003b 100644 --- a/content/openapi/types-and-parameters.md +++ b/content/openapi/types-and-parameters.md @@ -293,7 +293,7 @@ export class CreateCatDto {} > info **Hint** You only need to use `@ApiExtraModels()` once for a specific model class. -Alternatively, you can pass an options object with the `extraModels` property specified to the `SwaggerModule#createDocument()` method, as follows: +Alternatively, you can pass an options object with the `extraModels` property specified to the `SwaggerModule.createDocument()` method, as follows: ```typescript const documentFactory = () => diff --git a/content/recipes/cqrs.md b/content/recipes/cqrs.md index 0a61d24331..d67e5c445f 100644 --- a/content/recipes/cqrs.md +++ b/content/recipes/cqrs.md @@ -89,7 +89,9 @@ export class KillDragonCommand extends Command<{ constructor( public readonly heroId: string, public readonly dragonId: string, - ) {} + ) { + super(); + } } @@switch export class KillDragonCommand extends Command { diff --git a/content/recipes/mikroorm.md b/content/recipes/mikroorm.md index d6a384d325..a422b2951c 100644 --- a/content/recipes/mikroorm.md +++ b/content/recipes/mikroorm.md @@ -32,8 +32,7 @@ import { SqliteDriver } from '@mikro-orm/sqlite'; controllers: [AppController], providers: [AppService], }) -export class AppModule { -} +export class AppModule {} ``` The `forRoot()` method accepts the same configuration object as `init()` from the MikroORM package. Check [this page](https://mikro-orm.io/docs/configuration) for the complete configuration documentation. @@ -67,7 +66,7 @@ export class AppModule {} Afterward, the `EntityManager` will be available to inject across the entire project (without importing any module elsewhere). ```ts -// Import everytyhing from your driver package or `@mikro-orm/knex` +// Import everything from your driver package or `@mikro-orm/knex` import { EntityManager, MikroORM } from '@mikro-orm/sqlite'; @Injectable() @@ -246,7 +245,7 @@ The `@mikro-orm/nestjs` package exposes `getRepositoryToken()` function that ret PhotoService, { // or when you have a custom repository: `provide: PhotoRepository` - provide: getRepositoryToken(Photo), + provide: getRepositoryToken(Photo), useValue: mockedRepository, }, ], diff --git a/content/recipes/passport.md b/content/recipes/passport.md index 59f69eff4a..c9547b1fa6 100644 --- a/content/recipes/passport.md +++ b/content/recipes/passport.md @@ -372,7 +372,7 @@ async login(@Request() req) { #### Logout route -To log out, we can create an additional route that invokes `res.logout()` to clear the user's session. This is a typical approach used in session-based authentication, but it does not apply to JWTs. +To log out, we can create an additional route that invokes `req.logout()` to clear the user's session. This is a typical approach used in session-based authentication, but it does not apply to JWTs. ```typescript @UseGuards(LocalAuthGuard) diff --git a/content/security/authorization.md b/content/security/authorization.md index a5f7f8c7d6..b036f91607 100644 --- a/content/security/authorization.md +++ b/content/security/authorization.md @@ -234,14 +234,12 @@ With this in place, we can define the `createForUser()` method on the `CaslAbili ```typescript type Subjects = InferSubjects | 'all'; -export type AppAbility = Ability<[Action, Subjects]>; +export type AppAbility = MongoAbility<[Action, Subjects]>; @Injectable() export class CaslAbilityFactory { createForUser(user: User) { - const { can, cannot, build } = new AbilityBuilder< - Ability<[Action, Subjects]> - >(Ability as AbilityClass); + const { can, cannot, build } = new AbilityBuilder(createMongoAbility); if (user.isAdmin) { can(Action.Manage, 'all'); // read-write access to everything @@ -263,11 +261,13 @@ export class CaslAbilityFactory { > warning **Notice** `all` is a special keyword in CASL that represents "any subject". -> info **Hint** `Ability`, `AbilityBuilder`, `AbilityClass`, and `ExtractSubjectType` classes are exported from the `@casl/ability` package. +> info **Hint** Since CASL v6, `MongoAbility` serves as the default ability class, replacing the legacy `Ability` to better support condition-based permissions using MongoDB-like syntax. Despite the name, it is not tied to MongoDB — it works with any kind of data by simply comparing objects against conditions written in Mongo-like syntax. + +> info **Hint** `MongoAbility`, `AbilityBuilder`, `AbilityClass`, and `ExtractSubjectType` classes are exported from the `@casl/ability` package. > info **Hint** `detectSubjectType` option let CASL understand how to get subject type out of an object. For more information read [CASL documentation](https://casl.js.org/v6/en/guide/subject-type-detection#use-classes-as-subject-types) for details. -In the example above, we created the `Ability` instance using the `AbilityBuilder` class. As you probably guessed, `can` and `cannot` accept the same arguments but have different meanings, `can` allows to do an action on the specified subject and `cannot` forbids. Both may accept up to 4 arguments. To learn more about these functions, visit the official [CASL documentation](https://casl.js.org/v6/en/guide/intro). +In the example above, we created the `MongoAbility` instance using the `AbilityBuilder` class. As you probably guessed, `can` and `cannot` accept the same arguments but have different meanings, `can` allows to do an action on the specified subject and `cannot` forbids. Both may accept up to 4 arguments. To learn more about these functions, visit the official [CASL documentation](https://casl.js.org/v6/en/guide/intro). Lastly, make sure to add the `CaslAbilityFactory` to the `providers` and `exports` arrays in the `CaslModule` module definition: @@ -297,7 +297,7 @@ if (ability.can(Action.Read, 'all')) { } ``` -> info **Hint** Learn more about the `Ability` class in the official [CASL documentation](https://casl.js.org/v6/en/guide/intro). +> info **Hint** Learn more about the `MongoAbility` class in the official [CASL documentation](https://casl.js.org/v6/en/guide/intro). For example, let's say we have a user who is not an admin. In this case, the user should be able to read articles, but creating new ones or removing the existing articles should be prohibited. @@ -311,7 +311,7 @@ ability.can(Action.Delete, Article); // false ability.can(Action.Create, Article); // false ``` -> info **Hint** Although both `Ability` and `AbilityBuilder` classes provide `can` and `cannot` methods, they have different purposes and accept slightly different arguments. +> info **Hint** Although both `MongoAbility` and `AbilityBuilder` classes provide `can` and `cannot` methods, they have different purposes and accept slightly different arguments. Also, as we have specified in our requirements, the user should be able to update its articles: @@ -329,7 +329,7 @@ article.authorId = 2; ability.can(Action.Update, article); // false ``` -As you can see, `Ability` instance allows us to check permissions in pretty readable way. Likewise, `AbilityBuilder` allows us to define permissions (and specify various conditions) in a similar fashion. To find more examples, visit the official documentation. +As you can see, `MongoAbility` instance allows us to check permissions in pretty readable way. Likewise, `AbilityBuilder` allows us to define permissions (and specify various conditions) in a similar fashion. To find more examples, visit the official documentation. #### Advanced: Implementing a `PoliciesGuard` diff --git a/content/security/rate-limiting.md b/content/security/rate-limiting.md index 47ce1998bd..43cefcbb34 100644 --- a/content/security/rate-limiting.md +++ b/content/security/rate-limiting.md @@ -94,7 +94,7 @@ export class UsersController { } ``` -There is also the `@Throttle()` decorator which can be used to override the `limit` and `ttl` set in the global module, to give tighter or looser security options. This decorator can be used on a class or a function as well. With version 5 and onwards, the decorator takes in an object with the string relating to the name of the throttler set, and an object with the limit and ttl keys and integer values, similar to the options passed to the root module. If you do not have a name set in your original options, use the string `default` You have to configure it like this: +There is also the `@Throttle()` decorator which can be used to override the `limit` and `ttl` set in the global module, to give tighter or looser security options. This decorator can be used on a class or a function as well. With version 5 and onwards, the decorator takes in an object with the string relating to the name of the throttler set, and an object with the limit and ttl keys and integer values, similar to the options passed to the root module. If you do not have a name set in your original options, use the string `default`. You have to configure it like this: ```typescript // Override default configuration for Rate limiting and duration. @@ -112,7 +112,7 @@ If your application is running behind a proxy server, it’s essential to config Here's an example that demonstrates how to enable `trust proxy` for the Express adapter: ```typescript -@@filename(main.ts) +@@filename(main) import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { NestExpressApplication } from '@nestjs/platform-express'; @@ -238,7 +238,7 @@ The following options are valid for the object passed to the array of the `Throt - + @@ -354,9 +354,7 @@ For most people, wrapping your options in an array will be enough. If you are using a custom storage, you should wrap your `ttl` and `limit` in an array and assign it to the `throttlers` property of the options object. -Any `@ThrottleSkip()` should now take in an object with `string: boolean` props. -The strings are the names of the throttlers. If you do not have a name, pass the -string `'default'`, as this is what will be used under the hood otherwise. +Any `@SkipThrottle()` decorator can be used to bypass throttling for specific routes or methods. It accepts an optional boolean parameter, which defaults to `true`. This is useful when you want to skip rate limiting on particular endpoints. Any `@Throttle()` decorators should also now take in an object with string keys, relating to the names of the throttler contexts (again, `'default'` if no name) diff --git a/content/techniques/caching.md b/content/techniques/caching.md index 58e0a04a91..e3d49ad121 100644 --- a/content/techniques/caching.md +++ b/content/techniques/caching.md @@ -54,7 +54,7 @@ await this.cacheManager.set('key', 'value'); > warning **Note** The in-memory cache storage can only store values of types that are supported by [the structured clone algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#javascript_types). -You can manually specify a TTL (expiration time in miliseconds) for this specific key, as follows: +You can manually specify a TTL (expiration time in milliseconds) for this specific key, as follows: ```typescript await this.cacheManager.set('key', 'value', 1000); @@ -322,7 +322,7 @@ CacheModule.registerAsync({ This works the same as `useClass` with one critical difference - `CacheModule` will lookup imported modules to reuse any already-created `ConfigService`, instead of instantiating its own. -> info **Hint** `CacheModule#register` and `CacheModule#registerAsync` and `CacheOptionsFactory` has an optional generic (type argument) to narrow down store-specific configuration options, making it type safe. +> info **Hint** `CacheModule#register`, `CacheModule#registerAsync` and `CacheOptionsFactory` have an optional generic (type argument) to narrow down store-specific configuration options, making it type safe. You can also pass so-called `extraProviders` to the `registerAsync()` method. These providers will be merged with the module providers. diff --git a/content/techniques/compression.md b/content/techniques/compression.md index 46244a4769..63f653f4bb 100644 --- a/content/techniques/compression.md +++ b/content/techniques/compression.md @@ -33,9 +33,15 @@ $ npm i --save @fastify/compress Once the installation is complete, apply the `@fastify/compress` middleware as global middleware. +> warning **Warning** Please ensure, that you use the type `NestFastifyApplication` when creating the application. Otherwise, you cannot use `register` to apply the compression-middleware. + ```typescript +import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify'; + import compression from '@fastify/compress'; -// somewhere in your initialization file + +// inside bootstrap() +const app = await NestFactory.create(AppModule, new FastifyAdapter()); await app.register(compression); ``` diff --git a/content/techniques/configuration.md b/content/techniques/configuration.md index c0574bd8e1..7fefb23fc7 100644 --- a/content/techniques/configuration.md +++ b/content/techniques/configuration.md @@ -148,7 +148,7 @@ $ npm i js-yaml $ npm i -D @types/js-yaml ``` -Once the package is installed, we use `yaml#load` function to load YAML file we just created above. +Once the package is installed, we use the `yaml#load` function to load the YAML file we just created above. ```typescript @@filename(config/configuration) diff --git a/content/techniques/events.md b/content/techniques/events.md index e903aefcac..fef6e4d6a4 100644 --- a/content/techniques/events.md +++ b/content/techniques/events.md @@ -149,7 +149,7 @@ To avoid this issue, you can use the `waitUntilReady` method of the `EventEmitte ```typescript await this.eventEmitterReadinessWatcher.waitUntilReady(); -await this.eventEmitter.emit( +this.eventEmitter.emit( 'order.created', new OrderCreatedEvent({ orderId: 1, payload: {} }), ); diff --git a/content/techniques/file-upload.md b/content/techniques/file-upload.md index c36af51b3d..02663af9f7 100644 --- a/content/techniques/file-upload.md +++ b/content/techniques/file-upload.md @@ -131,9 +131,7 @@ export abstract class FileValidator> { `FileValidator` is a regular class that has access to the file object and validates it according to the options provided by the client. Nest has two built-in `FileValidator` implementations you can use in your project: - `MaxFileSizeValidator` - Checks if a given file's size is less than the provided value (measured in `bytes`) -- `FileTypeValidator` - Checks if a given file's mime-type matches the given value. - -> warning **Warning** To verify file type, [FileTypeValidator](https://github.com/nestjs/nest/blob/master/packages/common/pipes/file/file-type.validator.ts) class uses the type as detected by multer. By default, multer derives file type from file extension on user's device. However, it does not check actual file contents. As files can be renamed to arbitrary extensions, consider using a custom implementation (like checking the file's [magic number](https://www.ibm.com/support/pages/what-magic-number)) if your app requires a safer solution. +- `FileTypeValidator` - Checks if a given file's mime-type matches a given string or RegExp. By default, validates the mime-type using file content [magic number](https://www.ibm.com/support/pages/what-magic-number) To understand how these can be used in conjunction with the aforementioned `FileParsePipe`, we'll use an altered snippet of the last presented example: diff --git a/content/techniques/sql.md b/content/techniques/sql.md index 747d0c60c0..2d85596235 100644 --- a/content/techniques/sql.md +++ b/content/techniques/sql.md @@ -938,7 +938,7 @@ export class UsersService { > warning **Notice** Don't forget to import the `UsersModule` into the root `AppModule`. -If you want to use the repository outside of the module which imports `SequelizeModule.forFeature`, you'll need to re-export the providers generated by it. +If you want to use the model outside of the module which imports `SequelizeModule.forFeature`, you'll need to re-export the providers generated by it. You can do this by exporting the whole module, like this: ```typescript diff --git a/content/techniques/streaming-files.md b/content/techniques/streaming-files.md index 13bad1cadc..1c6ff70bff 100644 --- a/content/techniques/streaming-files.md +++ b/content/techniques/streaming-files.md @@ -46,7 +46,7 @@ export class FileController { } ``` -The default content type (the value for `Content-Type` HTTP response header) is `application/octet-stream`. If you need to customize this value you can use the `type` option from `StreamableFile`, or use the `res.set` method or the [`@Header()`](/controllers#headers) decorator, like this: +The default content type (the value for `Content-Type` HTTP response header) is `application/octet-stream`. If you need to customize this value you can use the `type` option from `StreamableFile`, or use the `res.set` method or the [`@Header()`](/controllers#response-headers) decorator, like this: ```ts import { Controller, Get, StreamableFile, Res } from '@nestjs/common'; diff --git a/content/techniques/task-scheduling.md b/content/techniques/task-scheduling.md index bf962d0757..6f9cf51224 100644 --- a/content/techniques/task-scheduling.md +++ b/content/techniques/task-scheduling.md @@ -1,4 +1,4 @@ -### Task Scheduling +### Task scheduling Task scheduling allows you to schedule arbitrary code (methods/functions) to execute at a fixed date/time, at recurring intervals, or once after a specified interval. In the Linux world, this is often handled by packages like [cron](https://en.wikipedia.org/wiki/Cron) at the OS level. For Node.js apps, there are several packages that emulate cron-like functionality. Nest provides the `@nestjs/schedule` package, which integrates with the popular Node.js [cron](https://github.com/kelektiv/node-cron) package. We'll cover this package in the current chapter. diff --git a/content/techniques/validation.md b/content/techniques/validation.md index 8b699d492a..a7f59fdc55 100644 --- a/content/techniques/validation.md +++ b/content/techniques/validation.md @@ -100,7 +100,7 @@ In addition to these, all `class-validator` options (inherited from the `Validat - + diff --git a/src/app/homepage/homepage.component.html b/src/app/homepage/homepage.component.html index a0b4335161..473382ffec 100644 --- a/src/app/homepage/homepage.component.html +++ b/src/app/homepage/homepage.component.html @@ -20,7 +20,7 @@

Support us

Nest is an MIT-licensed open source project. It can grow thanks to the - support by these awesome people. If you'd like to join them, please + support of these awesome people. If you'd like to join them, please read more here.

@@ -50,13 +50,6 @@

Principal Sponsors

class="logo-sponsor logo-sponsor--slim" /> --> - - Marblism Logo - Principal Sponsors class="logo-sponsor logo-sponsor--slim" /> - - Amplication Logo -

Sponsors / Partners

diff --git a/src/app/homepage/menu/menu.component.ts b/src/app/homepage/menu/menu.component.ts index 44cee88ab2..cbffca3aab 100644 --- a/src/app/homepage/menu/menu.component.ts +++ b/src/app/homepage/menu/menu.component.ts @@ -291,6 +291,10 @@ export class MenuComponent implements OnInit { isOpened: false, path: '/migration-guide', }, + { + title: 'API Reference', + externalUrl: 'https://api-references-nestjs.netlify.app/', + }, { title: 'Official courses', externalUrl: 'https://courses.nestjs.com/',
namethe name for internal tracking of which throttler set is being used. Defaults to `default` if not passedthe name for internal tracking of which throttler set is being used. Defaults to default if not passed
ttl
always booleanSet default for always option of decorators. Default can be overridden in decorator optionsSet default for always option of decorators. Default can be overridden in decorator options.