<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Провайдеры в Nest JS - 1.3]]></title><description><![CDATA[<p dir="auto">Провайдеры (<code>providers</code>) — ключевая концепция фреймворка. Базовые классы, такие как сервисы (<code>services</code>), фабрики (<code>factories</code>) и репозитории (<code>repositories</code>), могут быть провайдерами. Это одна из основных абстракций фреймворка Nest. Особенность провайдеров — возможность внедрения зависимостей. Это позволяет создавать сложные связи между объектами. Nest отвечает за создание экземпляров и передачу зависимостей.</p>
<p dir="auto"><img src="/assets/uploads/files/1731150359308-providers.jpg" alt="providers.jpg" class=" img-fluid img-markdown" /></p>
<h2>Провайдеры</h2>
<p dir="auto">Провайдер — это обычный класс (хотя не всегда так), который, например, может быть сервисом. Чтобы класс стал провайдером, его нужно пометить специальным декоратором и зарегистрировать.</p>
<h2>Провайдеры на практике</h2>
<p dir="auto">Многие базовые классы в Nest могут быть провайдерами. Рассмотрим это на простом примере. Предположим, мы создаём приложение для доски объявлений, где пользователи могут размещать объявления о продаже ненужных вещей, а другие могут комментировать эти объявления. Пример архитектуры приложения представлен на следующем рисунке.</p>
<p dir="auto"><img src="/assets/uploads/files/1731152067813-providers-1.jpg" alt="providers-1.jpg" class=" img-fluid img-markdown" /></p>
<p dir="auto">При разработке приложения логично разделить его на три модуля: объявления (<code>offers</code>), пользователи (<code>users</code>) и комментарии (<code>comments</code>). Каждый модуль будет иметь свой сервис для бизнес-логики и работы с базой данных.</p>
<p dir="auto">Представим задачу: удаление объявления должно автоматически удалять все связанные комментарии. Например, к объявлению с идентификатором <code>12</code> пользователи оставили <code>100</code> комментариев. Эти комментарии хранятся в отдельной коллекции и управляются отдельным сервисом. Нам нужно реализовать метод в контроллере <code>OfferController</code>, который будет удалять объявление и его комментарии.</p>
<p dir="auto">Одно из решений — использовать два сервиса (для объявлений и комментариев) в контроллере <code>OfferController</code>. Сервис комментариев предоставляет метод для удаления всех комментариев к определённому объявлению, а сервис объявлений — метод для удаления самого объявления. Таким образом, в контроллере <code>OfferController</code> понадобятся оба сервиса для выполнения задачи.</p>
<p dir="auto"><img src="/assets/uploads/files/1731152216995-providers-2.jpg" alt="providers-2.jpg" class=" img-fluid img-markdown" /></p>
<p dir="auto">Схематично взаимодействие контроллера и сервисов показано на следующем рисунке. Контроллеру нужны два сервиса: <code>OfferService</code> и <code>CommentService</code>. Их можно подключить напрямую или внедрить как зависимости. Здесь на помощь придут провайдеры. Многие базовые классы могут быть провайдерами, и <code>OfferService</code> и <code>CommentService</code> отлично подходят для этой роли.</p>
<h2>Внедрение зависимостей</h2>
<p dir="auto">Nest использует свой IoC-контейнер и паттерн внедрения зависимостей (DI). Фреймворк упрощает использование DI, предоставляя разработчикам более простой API. Важно следовать подходам и ограничениям, установленным фреймворком.</p>
<p dir="auto">Зависимость — это объект, от которого зависит другой объект. Например, контроллеру нужны классы-сервисы для работы.</p>
<p dir="auto">Теперь познакомимся с внедрением зависимостей и созданием провайдеров. Для создания провайдера нужно выполнить несколько шагов:</p>
<ul>
<li>Применить декоратор <code>@Injectable()</code> из модуля <code>@nestjs/common</code> к классу.</li>
<li>Этот декоратор помечает класс, экземпляры которого будут внедряться как зависимости. Он принимает объект настроек, который опционален. Позже мы разберёмся с ним, а сейчас рассмотрим пример применения декоратора.</li>
</ul>
<p dir="auto">Ниже приведён фрагмент кода с базовой реализацией сервисов для нашего приложения:</p>
<pre><code class="language-ts">  @Injectable()
  export class OfferService {
    private offers: Offer[];
    public async create() { /* Не реализовано */ }
    public async deleteById(offerId: number) { /* Не реализовано */ }
  }

  @Injectable()
  export class CommentService {
    private comments: Comment[];
    public async create() { /* Не реализовано */ }
    public async deleteComments(offerId: number) { /* Не реализовано */ }
  }
</code></pre>
<p dir="auto">Когда класс помечен декоратором <code>@Injectable</code>, это означает, что им может управлять встроенный IoC-контейнер Nest. Он будет создавать и хранить экземпляры класса, избавляя нас от необходимости делать это вручную.</p>
<h2>Внедрение зависимости в конструктор</h2>
<p dir="auto">Следующий шаг — внедрить провайдер как зависимость. Здесь проявляется «магия» Nest. Чтобы внедрить зависимость через параметр конструктора, не нужно делать ничего особенного. Просто укажите тип параметра, а Nest сам разрешит зависимость, создаст новый или переиспользует существующий экземпляр и передаст его в параметр. В коде контроллера это может выглядеть так:</p>
<pre><code class="language-ts">@Controller('offer')
export class OfferController {
  constructor(
    // Внедряем зависимости
    private readonly commentService: CommentService,
    private readonly offerService: OfferService,
  ) {}

  @Delete('/:offerId')
  public delete() {}
}
</code></pre>
<p dir="auto">Обратите внимание на параметры <code>commentService</code> и <code>offerService</code> в конструкторе. Дополнительные декораторы для внедрения не нужны — Nest сам разрешит зависимости по типу. Главное, чтобы нужный провайдер был доступен в модуле.</p>
<h2>Регистрация провайдера</h2>
<p dir="auto">Регистрация провайдера происходит в секции providers декоратора <code>@Module</code>. Это массив, в котором перечисляются все провайдеры модуля. На этом этапе класс регистрируется в IoC-контейнере. Для регистрации достаточно указать имена классов, без выделения токена (более сложные случаи рассмотрим позже):</p>
<pre><code class="language-ts">@Module({
  // Регистрируем в IoC-контейнере
  providers: [OfferService],
  controllers: [OfferController],

  // Импортируем модуль
  // Позже разберём причины импорта модуля
  imports: [CommentModule]
})
export class OfferModule {}
</code></pre>
<p dir="auto">Обратите внимание на ситуации, когда модулю нужен провайдер из другого модуля. Как мы уже упоминали, «провайдер должен быть доступен в области видимости модуля, в котором используется». Провайдеры одного модуля инкапсулированы, что означает, что один модуль не может получить доступ к провайдерам другого. Например, модулю <code>OfferModule</code> нужен провайдер из <code>CommentModule</code>.</p>
<p dir="auto">IoC-контейнер можно представить как коробку, в которой аккуратно хранятся классы для внедрения зависимостей. Он отвечает за создание экземпляров классов и разрешение вложенных зависимостей.</p>
<p dir="auto">Важно не забыть экспортировать провайдеры. Это делается через декоратор <code>@Module</code>. Рассмотрим это на примере модуля <code>CommentModule</code>:</p>
<pre><code class="language-ts">@Module({
  controllers: [CommentController],
  providers: [CommentService],

  // Экспортируем провайдер
  exports: [CommentService],
})
export class CommentModule {}
</code></pre>
<p dir="auto">Обратите внимание на секцию <code>exports</code>. Здесь перечислены сервисы (провайдеры), доступные другим модулям при импорте <code>CommentModule</code>. В декораторе мы также определяем секцию imports, где импортируем <code>CommentModule</code>.</p>
<p dir="auto">Важно запомнить: модуль экспортирует провайдеры. Если другой модуль хочет использовать провайдер, ему нужно просто импортировать модуль с этими провайдерами.</p>
<p dir="auto">Вернёмся к нашему примеру: модуль <code>CommentModule</code> экспортирует провайдер <code>CommentService</code>, который нужен в контроллере модуля <code>OfferModule</code>. Поэтому модуль объявлений импортирует <code>CommentModule</code>.</p>
<p dir="auto">На этом процесс работы с провайдерами завершён. Nest автоматически внедрит необходимые провайдеры в зависимости. Вкратце: используем декоратор <code>@Injectable</code>, следим за доступностью провайдера и внедряем зависимость через параметры конструктора.</p>
<p dir="auto"><img src="/assets/uploads/files/1731152796756-photo_2024-11-09_14-46-10.jpg" alt="photo_2024-11-09_14-46-10.jpg" class=" img-fluid img-markdown" /></p>
<p dir="auto">Если следовать интерфейсу <code>ClassProvider</code>, то запись должна содержать два свойства: <code>useClass</code> (ссылка на класс) и <code>provide</code> (токен). По этому токену механизм DI сможет получить нужные данные, например, экземпляр класса. Давайте применим это к нашему примеру:</p>
<pre><code class="language-ts">@Module({
  controllers: [UsersController],
  providers: [
    {
      provide: UserService,
      useClass: UserService,
    },
  ],
  exports: [UserService],
})
export class UsersModule {}
</code></pre>
<p dir="auto">Посмотрев на полную запись, становится яснее, что для регистрации класса в IoC-контейнере нужен токен. В нашем примере мы используем <code>UserService</code> в качестве токена, связывая его с классом<code>UserService</code>. Когда Nest будет искать зависимость, он будет искать её именно по этому токену.</p>
<p dir="auto">Разрешение зависимостей не так просто, как кажется. Nest старается упростить эту задачу для разработчиков, строя граф зависимостей на этапе загрузки приложения. Этот граф содержит информацию обо всех зависимостях и гарантирует правильный порядок их разрешения.</p>
<h2>Заключение</h2>
<p dir="auto">Провайдеры — это абстракция, которая позволяет внедрять классы как зависимости. Многие стандартные классы могут быть провайдерами. Nest упрощает внедрение: достаточно пометить класс декоратором <code>@Injectable</code> и зарегистрировать его в секции <code>providers</code> декоратора <code>@Module</code>. Для внедрения через конструктор достаточно указать параметр нужного типа — остальное сделает Nest.</p>
]]></description><link>https://forum.exlends.ru/topic/37/provajdery-v-nest-js-1-3</link><generator>RSS for Node</generator><lastBuildDate>Wed, 20 May 2026 08:57:16 GMT</lastBuildDate><atom:link href="https://forum.exlends.ru/topic/37.rss" rel="self" type="application/rss+xml"/><pubDate>Sat, 09 Nov 2024 11:52:19 GMT</pubDate><ttl>60</ttl></channel></rss>