Информация о группе Закрытая

administrators

Список участников
  • Функции в JavaScript для чайников

    Привет, друзья! 👋 Сегодня мы погрузимся в одну из основополагающих концепций JavaScript — функции. Если вы только начинаете свой путь в программировании, этот пост для вас!

    Что такое функция?

    Функция — это блок кода, который можно вызывать по имени. Она выполняет определённую задачу и может принимать входные данные, а также возвращать результат. Функции помогают организовать код и делают его более читаемым.

    Зачем нужны функции?

    • Повторное использование кода: Вы можете использовать одну и ту же функцию в разных частях программы.
    • Организация кода: Функции помогают структурировать код, делая его более понятным.
    • Инкапсуляция логики: Вы можете скрыть сложные операции внутри функции и предоставить простой интерфейс.

    Как объявить функцию?

    Есть несколько способов объявить функцию в JavaScript. Рассмотрим три наиболее распространённых:

    1. Функциональное выражение

    javascript
    const greet = function(name) {
        return `Привет, ${name}!`;
    };
    
    console.log(greet('Мир')); // Привет, Мир!
    

    2. Функция-объявление

    function add(a, b) {
        return a + b;
    }
    
    console.log(add(3, 4)); // 7
    

    3. Стрелочная функция

    const multiply = (x, y) => x * y;
    
    console.log(multiply(5, 6)); // 30
    

    Аргументы и параметры

    Функции могут принимать аргументы — значения, которые передаются в функцию при её вызове. Параметры — это переменные, определенные в объявлении функции.

    function subtract(a, b) {
        return a - b;
    }
    
    console.log(subtract(10, 4)); // 6
    

    Возврат значений

    Функция может возвращать значение с помощью ключевого слова return. Если функция не возвращает значение, по умолчанию возвращается undefined.

    function square(num) {
        return num * num;
    }
    
    const result = square(8);
    console.log(result); // 64
    

    Область видимости

    Область видимости определяет, где переменные доступны. В JavaScript есть две основные области видимости:

    • Глобальная область видимости: Переменные, объявленные вне функций, доступны везде.
    • Локальная область видимости: Переменные, объявленные внутри функции, доступны только внутри этой функции.
    let globalVar = 'Я глобальная!';
    
    function showVar() {
        let localVar = 'Я локальная!';
        console.log(globalVar); // Доступно
        console.log(localVar); // Доступно
    }
    
    showVar();
    console.log(globalVar); // Доступно
    // console.log(localVar); // Ошибка: localVar не определена
    

    Резюме

    Функции — это мощный инструмент в JavaScript, который позволяет вам писать чистый и организованный код. Не бойтесь экспериментировать с ними и использовать их в своих проектах!

    Если у вас есть вопросы или вы хотите обсудить другие темы, оставляйте комментарии! 💬

    написал в JavaScript
  • Как использовать модули в JavaScript?
    1. Экспортирование модулей: Позволяет делать функции, объекты или переменные доступными для других файлов. Используется export для экспорта нескольких элементов из модуля.
    // utils.js
    export function add(a, b) {
      return a + b;
    }
    export const pi = 3.14159;
    
    1. Импортирование модулей: Позволяет использовать ранее экспортированные элементы. Используется import для импорта отдельных элементов из модуля.
    // main.js
    import { add, pi } from './utils.js';
    console.log(add(2, 3)); // 5
    console.log(`Value of pi: ${pi}`); // Value of pi: 3.14159
    
    1. Экспорт по умолчанию: Используется для экспорта одного основного элемента из модуля. Это может быть функция, класс или объект. Используется export default для этого.
    // math.js
    export default function multiply(a, b) {
      return a * b;
    }
    
    1. Импорт по умолчанию: Осуществляется без фигурных скобок. Можно дать импортируемому элементу любое имя.
    // main.js
    import multiply from './math.js';
    console.log(multiply(2, 3)); // 6
    
    1. Динамический импорт: Позволяет загружать модули асинхронно, что полезно для загрузки модулей по требованию. Используется import() для динамического импорта.
    // main.js
    async function loadModule() {
      const { add } = await import('./utils.js');
      console.log(add(5, 10)); // 15
    }
    loadModule();
    
    1. Импорт всего содержимого модуля:
      Иногда нужно импортировать все содержимое модуля под одним именем. Используется import * as для этого.
    // main.js
    import * as Utils from './utils.js';
    console.log(Utils.add(2, 3)); // 5
    console.log(`Value of pi: ${Utils.pi}`); // Value of pi: 3.14159
    
    написал в JavaScript
  • Свойство "id" не существует в типе "string | object | Buffer".

    Следующая проблема с которой я столкнулся, заключается в типах модулей (хотя я явно указал типы в user.d.ts), а именно когда мы получаем request.user:

    const getUser = async (request: FastifyRequest, reply: FastifyReply) => {
      try {
        const userId = (request.user as { id: string }).id;
        const user = await userService.findOneByUserId(userId);
        reply.status(200).send(user);
      } catch (err) {
        throw new Error("Пользователь с указанным id не найден");
      }
    };
    

    В принципе в этом же коде и кроется ответ: (const userId = (request.user as { id: string }).id), явно указать что в request.user всегда будет id: string.

    Я также нашел обсуждение на github на эту тему - ссылочка, хотя проблема старая, судя по всему один хер она почему-то нормально не фиксится.

    Надеюсь это кому-то поможет 🙂

    написал в Fastify
  • RE: Fastify - No Authorization was found in request.headers

    Кароче решил проблему, все регистрировалось корректно - это я про регистрацию плагина и прочее.

    Вообщем, я забывал передавать в заголовке сам токен когда проверял защищенные роуты

    Снимок экрана 2024-11-09 в 23.34.18.png

    Проблема решена!)

    написал в Fastify
  • Fastify - No Authorization was found in request.headers

    Кароче, попробовал я этот ваш Fastify, бля ну все шло гладко, уже там и репозитории и логику в сервисах прикрути.

    Дело дошло до регистрации и авторизации пользователей. И тут начались проблемы, эта дура по непонятным мне приничал нихуя не регистрирует заголовок Authentication.
    Кстати в fastify.d.ts я тоже все четко прописал по инструкции:

    import 'fastify';
    
    declare module 'fastify' {
    interface FastifyInstance {
        authenticate: (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
      }
    }
    

    Так вот эта сука, отдает токен можно посмотреть в fastifyInstance.post('/token'...
    Казалось бы заебись, но по непонятным мне причинам не записывает нихуя в заголовок Authentication, я уже и так и сяк, но не пойму почему.

    Вынес пока все дерьмище в index.ts, может подскажет кто нибудь в чем может быть причина ? Молю 😥

    import fastifyInstance from "./infrastructure/server/fastify";
    import { registerRoutes } from "./app/app-module";
    import { dataSourceInit } from "./infrastructure/lib/db/postgresql/connect-typeorm";
    import fastifyFormbody from "@fastify/formbody";
    import fastifyJwt from "@fastify/jwt";
    import fp from 'fastify-plugin';
    
    (async () => {
      try {
        
        fastifyInstance.register(fastifyFormbody);
        
        const authenticate = fp(async function (instance, opts) {
          instance.register(fastifyJwt, {
            secret: 'super-strong-secret',
            sign: { expiresIn: '7d' }
          });
          
          instance.decorate('authenticate', async function (request, reply) {
            try {
              console.log('Authorization header:', request.headers.authorization);
              await request.jwtVerify();
              console.log('User verified:', request.user);
            } catch (err) {
              console.log('Authentication error:', err);
              reply.send(err);
            }
          });
        });
        
        fastifyInstance.register(authenticate);
        
        fastifyInstance.post('/token', async (request, reply) => {
          const user = "user=exampleUser"
          if (!user) {
            reply.code(400).send({ error: 'User is required' });
          }
          const token = fastifyInstance.jwt.sign({ user });
          return { token };
        });
        
        fastifyInstance.get('/', { onRequest: fastifyInstance.authenticate }, async function (request, reply) {
          console.log(request.user);
          
          return request.user;
        });
        
        fastifyInstance.get("/protected", { onRequest: fastifyInstance.authenticate }, async (request, reply) => {
          try {
            return { message: "You are authenticated!", user: request.user };
          } catch (err) {
            return { message: "хуй за щеку" };
          }
        });
        
        fastifyInstance.register(registerRoutes);
        
        await fastifyInstance.listen({ port: 3009 });
        fastifyInstance.ready((err) => {
          dataSourceInit();
          if (err) {
            fastifyInstance.log.error(err);
            process.exit(1);
          } else {
            const address = fastifyInstance.server.address();
            if (typeof address === "string") {
              console.log(`Fastify прослушивание сокета ${address}`);
            } else {
              console.log(`Fastify прослушивание http://localhost:${address.port}`);
            }
          }
        });
      } catch (err) {
        fastifyInstance.log.error(err);
        process.exit(1);
      }
    })();
    
    написал в Fastify
  • Провайдеры в Nest JS - 1.3

    Провайдеры (providers) — ключевая концепция фреймворка. Базовые классы, такие как сервисы (services), фабрики (factories) и репозитории (repositories), могут быть провайдерами. Это одна из основных абстракций фреймворка Nest. Особенность провайдеров — возможность внедрения зависимостей. Это позволяет создавать сложные связи между объектами. Nest отвечает за создание экземпляров и передачу зависимостей.

    providers.jpg

    Провайдеры

    Провайдер — это обычный класс (хотя не всегда так), который, например, может быть сервисом. Чтобы класс стал провайдером, его нужно пометить специальным декоратором и зарегистрировать.

    Провайдеры на практике

    Многие базовые классы в Nest могут быть провайдерами. Рассмотрим это на простом примере. Предположим, мы создаём приложение для доски объявлений, где пользователи могут размещать объявления о продаже ненужных вещей, а другие могут комментировать эти объявления. Пример архитектуры приложения представлен на следующем рисунке.

    providers-1.jpg

    При разработке приложения логично разделить его на три модуля: объявления (offers), пользователи (users) и комментарии (comments). Каждый модуль будет иметь свой сервис для бизнес-логики и работы с базой данных.

    Представим задачу: удаление объявления должно автоматически удалять все связанные комментарии. Например, к объявлению с идентификатором 12 пользователи оставили 100 комментариев. Эти комментарии хранятся в отдельной коллекции и управляются отдельным сервисом. Нам нужно реализовать метод в контроллере OfferController, который будет удалять объявление и его комментарии.

    Одно из решений — использовать два сервиса (для объявлений и комментариев) в контроллере OfferController. Сервис комментариев предоставляет метод для удаления всех комментариев к определённому объявлению, а сервис объявлений — метод для удаления самого объявления. Таким образом, в контроллере OfferController понадобятся оба сервиса для выполнения задачи.

    providers-2.jpg

    Схематично взаимодействие контроллера и сервисов показано на следующем рисунке. Контроллеру нужны два сервиса: OfferService и CommentService. Их можно подключить напрямую или внедрить как зависимости. Здесь на помощь придут провайдеры. Многие базовые классы могут быть провайдерами, и OfferService и CommentService отлично подходят для этой роли.

    Внедрение зависимостей

    Nest использует свой IoC-контейнер и паттерн внедрения зависимостей (DI). Фреймворк упрощает использование DI, предоставляя разработчикам более простой API. Важно следовать подходам и ограничениям, установленным фреймворком.

    Зависимость — это объект, от которого зависит другой объект. Например, контроллеру нужны классы-сервисы для работы.

    Теперь познакомимся с внедрением зависимостей и созданием провайдеров. Для создания провайдера нужно выполнить несколько шагов:

    • Применить декоратор @Injectable() из модуля @nestjs/common к классу.
    • Этот декоратор помечает класс, экземпляры которого будут внедряться как зависимости. Он принимает объект настроек, который опционален. Позже мы разберёмся с ним, а сейчас рассмотрим пример применения декоратора.

    Ниже приведён фрагмент кода с базовой реализацией сервисов для нашего приложения:

      @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) { /* Не реализовано */ }
      }
    

    Когда класс помечен декоратором @Injectable, это означает, что им может управлять встроенный IoC-контейнер Nest. Он будет создавать и хранить экземпляры класса, избавляя нас от необходимости делать это вручную.

    Внедрение зависимости в конструктор

    Следующий шаг — внедрить провайдер как зависимость. Здесь проявляется «магия» Nest. Чтобы внедрить зависимость через параметр конструктора, не нужно делать ничего особенного. Просто укажите тип параметра, а Nest сам разрешит зависимость, создаст новый или переиспользует существующий экземпляр и передаст его в параметр. В коде контроллера это может выглядеть так:

    @Controller('offer')
    export class OfferController {
      constructor(
        // Внедряем зависимости
        private readonly commentService: CommentService,
        private readonly offerService: OfferService,
      ) {}
    
      @Delete('/:offerId')
      public delete() {}
    }
    

    Обратите внимание на параметры commentService и offerService в конструкторе. Дополнительные декораторы для внедрения не нужны — Nest сам разрешит зависимости по типу. Главное, чтобы нужный провайдер был доступен в модуле.

    Регистрация провайдера

    Регистрация провайдера происходит в секции providers декоратора @Module. Это массив, в котором перечисляются все провайдеры модуля. На этом этапе класс регистрируется в IoC-контейнере. Для регистрации достаточно указать имена классов, без выделения токена (более сложные случаи рассмотрим позже):

    @Module({
      // Регистрируем в IoC-контейнере
      providers: [OfferService],
      controllers: [OfferController],
    
      // Импортируем модуль
      // Позже разберём причины импорта модуля
      imports: [CommentModule]
    })
    export class OfferModule {}
    

    Обратите внимание на ситуации, когда модулю нужен провайдер из другого модуля. Как мы уже упоминали, «провайдер должен быть доступен в области видимости модуля, в котором используется». Провайдеры одного модуля инкапсулированы, что означает, что один модуль не может получить доступ к провайдерам другого. Например, модулю OfferModule нужен провайдер из CommentModule.

    IoC-контейнер можно представить как коробку, в которой аккуратно хранятся классы для внедрения зависимостей. Он отвечает за создание экземпляров классов и разрешение вложенных зависимостей.

    Важно не забыть экспортировать провайдеры. Это делается через декоратор @Module. Рассмотрим это на примере модуля CommentModule:

    @Module({
      controllers: [CommentController],
      providers: [CommentService],
    
      // Экспортируем провайдер
      exports: [CommentService],
    })
    export class CommentModule {}
    

    Обратите внимание на секцию exports. Здесь перечислены сервисы (провайдеры), доступные другим модулям при импорте CommentModule. В декораторе мы также определяем секцию imports, где импортируем CommentModule.

    Важно запомнить: модуль экспортирует провайдеры. Если другой модуль хочет использовать провайдер, ему нужно просто импортировать модуль с этими провайдерами.

    Вернёмся к нашему примеру: модуль CommentModule экспортирует провайдер CommentService, который нужен в контроллере модуля OfferModule. Поэтому модуль объявлений импортирует CommentModule.

    На этом процесс работы с провайдерами завершён. Nest автоматически внедрит необходимые провайдеры в зависимости. Вкратце: используем декоратор @Injectable, следим за доступностью провайдера и внедряем зависимость через параметры конструктора.

    photo_2024-11-09_14-46-10.jpg

    Если следовать интерфейсу ClassProvider, то запись должна содержать два свойства: useClass (ссылка на класс) и provide (токен). По этому токену механизм DI сможет получить нужные данные, например, экземпляр класса. Давайте применим это к нашему примеру:

    @Module({
      controllers: [UsersController],
      providers: [
        {
          provide: UserService,
          useClass: UserService,
        },
      ],
      exports: [UserService],
    })
    export class UsersModule {}
    

    Посмотрев на полную запись, становится яснее, что для регистрации класса в IoC-контейнере нужен токен. В нашем примере мы используем UserService в качестве токена, связывая его с классомUserService. Когда Nest будет искать зависимость, он будет искать её именно по этому токену.

    Разрешение зависимостей не так просто, как кажется. Nest старается упростить эту задачу для разработчиков, строя граф зависимостей на этапе загрузки приложения. Этот граф содержит информацию обо всех зависимостях и гарантирует правильный порядок их разрешения.

    Заключение

    Провайдеры — это абстракция, которая позволяет внедрять классы как зависимости. Многие стандартные классы могут быть провайдерами. Nest упрощает внедрение: достаточно пометить класс декоратором @Injectable и зарегистрировать его в секции providers декоратора @Module. Для внедрения через конструктор достаточно указать параметр нужного типа — остальное сделает Nest.

    написал в NestJs
  • Модули в Nest JS - 1.2

    Модуль — это важная часть приложения на Nest. Каждое приложение начинается с корневого модуля, который Nest использует для создания структуры приложения. Эта структура помогает управлять зависимостями между модулями и провайдерами.

    На практике это проще, чем кажется. Модули позволяют разработчикам разбивать приложение на отдельные части. Представьте, что каждый модуль — это компонент вашего приложения. Например, один модуль может отвечать за управление заказами в интернет-магазине, а другой — за управление пользователями. Вместе эти компоненты создают функциональное приложение.

    Важно помнить, что модули в Nest не обязательно должны быть отдельными файлами. Это всего лишь абстракция, которая помогает структурировать приложение. Некоторые модули могут быть большими, другие — маленькими. Используя паттерн «композиция», разработчики могут создавать сложные модули из простых.

    Хотя Nest позволяет создать приложение с одним модулем, это подходит только для очень простых проектов. С увеличением сложности приложения становится очевидным, что использование нескольких модулей — более эффективный подход.

    modules.jpg

    Примечание: Абстракция «Модуль» в Nest реализует одноимённый структурный паттерн проектирования. Модуль объединяет данные, функции и физические компоненты в одну сущность, а взаимодействовать с ней можно через публичный интерфейс.

    Как работать с модулями в Nest

    В Nest модуль — это класс, помеченный декоратором @Module. Этот декоратор содержит метаданные, которые Nest использует для организации приложения.

    Декоратор @Module принимает объект, который описывает структуру модуля. В этом объекте можно указать провайдеры и контроллеры модуля, а также модули, от которых он зависит, и провайдеры, которые он экспортирует. Вот основные свойства:

    Свойство объекта Описание
    providers Список провайдеров модуля.
    controllers Список контроллеров модуля.
    imports Список импортируемых модулей.
    exports Список экспортируемых провайдеров модуля.

    Особое внимание стоит уделить свойствам imports и exports.

    • imports: здесь указываются модули, которые нужны текущему модулю. Если модуль использует функциональность других модулей, их нужно перечислить в этом свойстве. Импортированные модули могут использовать экспортируемые провайдеры.

    • exports: в этом свойстве перечисляются провайдеры, которые будут доступны другим модулям при импорте. Это позволяет делиться функциональностью между модулями.

    По умолчанию все провайдеры модуля скрыты. Чтобы использовать провайдер в другом модуле, его нужно экспортировать через свойство exports. Таким образом, экспортируемые провайдеры можно считать публичным интерфейсом модуля.

    Модули на практике

    Чтобы понять, как работают модули в Nest, рассмотрим пример простого приложения — API для управления пользователями. В этом приложении нам понадобятся функции для запуска сервера и управления пользователями.

    Для REST API создадим контроллер UserController, который будет обрабатывать маршруты. Если у нас есть бизнес-логика и взаимодействие с базой данных, нам понадобится модуль UserService, который будет реализовывать эту логику. Также потребуется описание сущности «Пользователь», например, UserEntity.

    Каждый из этих элементов будет храниться в отдельном файле, что делает структуру проекта более удобной и понятной. Примерная схема файловой структуры может выглядеть следующим образом:

    modules-1.jpg

    Давайте рассмотрим, что общего у контроллера, сервиса и сущности — все они относятся к одной области, связанной с управлением пользователями. Такие области называются доменами.

    Примечание: Домен — это предметная область, которая включает бизнес-логику, процессы, данные и терминологию, связанные с этой областью.

    Поскольку все эти элементы связаны, имеет смысл объединить их в функциональный модуль. Модуль «Пользователи» будет отвечать за работу с пользователями и предоставит публичный интерфейс для взаимодействия с другими модулями. Разработчик сможет использовать функциональность модуля, не углубляясь в детали его реализации.

    С ростом приложения появятся и другие модули, например, для управления заказами или отправкой уведомлений. Разделение на функциональные модули упростит поддержку и установит четкие границы для каждого из них.

    Примечание: Nest CLI позволяет быстро создать модуль с помощью команды: nest g module users.

    Пример организации кода для нашего модуля может выглядеть так:

    import { Module } from '@nestjs/common';
    import { UserController } from './user.controller';
    import { UserService } from './user.service';
    
    @Module({
      controllers: [UserController],
      providers: [UserService],
    })
    export class UserModule {}
    

    Новый модуль описывается как класс, например, UserModule, и помечается декоратором @Module. В этот декоратор передаётся объект с настройками модуля, где мы регистрируем контроллеры и провайдеры.

    Наш модуль пока ничего не экспортирует, но при необходимости можно добавить сервисы для использования в других модулях. Для этого в объект настроек нужно добавить свойство exports. Мы рассмотрим это на практике позже.

    Примечание: Хороший модуль должен решать одну конкретную задачу. Не стоит объединять всю функциональность приложения в одном модуле. Лучше разбивать на несколько, минимизируя зависимости между модулями.

    modules-2.jpg

    Для каждого модуля обычно создаётся отдельная директория, где размещаются все его компоненты: сервисы, контроллеры и вспомогательные скрипты. Это позволяет удобно организовать код и иметь всё, что связано с модулем, в одном месте.

    Подключение модуля

    Чтобы использовать функциональность модуля, его нужно подключить в другом модуле. Как уже упоминалось, любое приложение на Nest состоит как минимум из одного модуля, который называется корневым (обычно AppModule). Чтобы активировать функциональность UserModule, его необходимо подключить к корневому модулю приложения.

    // Файл app.module.ts
    
    import { Module } from '@nestjs/common';
    import { UserModule } from './cats/user.module';
    
    @Module({
      imports: [UserModule],
    })
    export class AppModule {}
    

    После подключения модуля UserModule, его функциональность становится активной. Обработчики маршрутов в контроллере UserController будут срабатывать при совпадении маршрута.

    Переиспользование модулей

    Создание и подключение модулей в Nest довольно просто. Разработчику нужно лишь следовать публичным интерфейсам. Nest автоматически управляет экземплярами классов модулей, и вам не нужно создавать их вручную.

    Все модули в Nest по умолчанию являются синглтонами. Это значит, что один и тот же экземпляр провайдера можно использовать в разных модулях. Таким образом, каждый модуль может быть повторно использован другими модулями.

    Например, если нужно разделить экземпляр UserService между модулями, просто экспортируйте этот провайдер из UserModule. Для этого добавьте соответствующий ключ в объект настроек модуля:

    import { Module } from '@nestjs/common';
    import { UserController } from './user.controller';
    import { UserService } from './user.service';
    
    @Module({
      controllers: [UserController],
      providers: [UserService],
      
      // Добавляем экспорт провайдера
      exports: [UserService]
    })
    export class UserModule {}
    

    Теперь любой модуль, который импортирует UserModule, получает доступ к UserService, используя один и тот же экземпляр.

    Реэкспорт модулей

    Модули могут экспортировать не только свои провайдеры, но и другие модули, которые они импортируют. Это называется реэкспортом модулей и значительно расширяет возможности для композиции. В коде реэкспорт модулей работает так же, как экспорт провайдеров. Рассмотрим это на примере.

    @Module({
      imports: [FooModule],
      exports: [FooModule]
    })
    export class UserModule {}
    

    Принцип остаётся прежним: в exports можно перечислять как провайдеры, так и модули для реэкспорта. В приведённом примере UserModule импортирует FooModule и реэкспортирует его, делая доступным для других модулей, которые импортируют UserModule.

    Внедрение зависимостей

    В класс модуля можно внедрять провайдеров как зависимости через конструктор. Это может быть полезно при настройке модуля. Рассмотрим это на примере:

    import { Module } from '@nestjs/common';
    import { UserController } from './user.controller';
    import { UserService } from './user.service';
    
    @Module({
      controllers: [UserController],
      providers: [UserService],
    })
    export class UserModule {
      // UserService внедряется с помощью DI
      constructor(private userService: UserService) {}
    }
    

    Важно помнить, что классы модулей не могут быть провайдерами из-за возможных циклических зависимостей.

    Глобальные модули

    В приложении часто появляются модули, которые нужно импортировать в разных местах, например, хелперы или модули для работы с базой данных. Чтобы не повторять процедуру импорта, Nest позволяет создавать глобальные модули. Все провайдеры таких модулей будут доступны во всем приложении.

    Чтобы сделать модуль глобальным, нужно аннотировать его декоратором @Global. Глобальные модули регистрируются только один раз, обычно в корневом модуле приложения.

    Рассмотрим пример с модулем UserModule. Аннотируем его декоратором @Global, и теперь провайдер UserService станет доступен другим модулям для внедрения без необходимости импортировать UserModule.

    import { Module, Global } from '@nestjs/common';
    import { UserController } from './user.controller';
    import { UserService } from './user.service';
    
    // Сделаем модуль глобальным
    @Global()
    @Module({
      controllers: [UserController],
      providers: [UserService],
    
      // Экспортируемые провайдеры
      // доступны другим модулям для внедрения
      // в качестве зависимости без необходимости
      // импортировать UserModule.
      exports: [UserService],
    })
    export class UserModule {}
    

    Хотя создание глобальных модулей удобно, использовать эту возможность нужно осторожно. Не стоит делать все модули глобальными, так как это может привести к сильной связанности между ними. Когда приложение вырастет, будет трудно отслеживать связи между модулями, и рефакторинг станет сложным. Используйте глобальные модули с осторожностью.

    Заключение

    Модуль — это ключевая абстракция в Nest. Его можно сравнить с компонентом, отвечающим за определённую функциональность приложения. Мы рассмотрели, как объявлять модули, использовать декоратор @Module, выполнять реэкспорт модулей и другие аспекты.

    Любое Nest-приложение состоит минимум из одного модуля, но не стоит складывать всю функциональность в один модуль. Разделение на модули упрощает поддержку и выделяет функциональность в отдельные компоненты.

    написал в NestJs
  • Nest CLI руководство- 1.1

    Для удобной работы с Nest есть дополнительный инструмент — Nest CLI. Это приложение для командной строки, которое упрощает использование фреймворка и помогает автоматизировать часто выполняемые задачи. Например, с его помощью вы можете:

    • Сгенерировать основу для проекта.
    • Подготовить шаблоны для контроллеров, моделей и других компонентов.

    Проще говоря, Nest CLI создаёт необходимые файлы и, в некоторых случаях, подключает их к модулю приложения.

    Установка

    Nest CLI — это обычный npm-пакет. Чтобы начать его использовать, нужно его установить. Есть несколько способов это сделать, но самый простой — установить пакет глобально. Для этого выполните команду:

    npm install -g @nestjs/cli
    

    После выполнения этой команды Nest CLI будет доступен для использования. Чтобы проверить, что установка прошла успешно, введите в терминале:

    nest --help
    

    Если всё установлено правильно, вы увидите список доступных команд:

    photo_2024-11-08_11-16-46.jpg

    Помимо глобальной установки, вы можете использовать утилиту npx. Например, вы можете установить @nestjs/cli как dev-зависимость и запускать его с помощью npx. Этот способ часто предпочтительнее, так как позволяет контролировать версию Nest CLI и обновлять её только для конкретного проекта.

    Основные команды Nest CLI

    • new [options] [name] — генерирует каркас для нового приложения на Nest.
    • build [options] [app] — выполняет сборку приложения.
    • start [options] [app] — запускает приложение.
    • info — выводит информацию о проекте.
    • add [options] <library> — добавляет внешнюю библиотеку к проекту.
    • generate [options] <schematic> [name] [path] — формирует заготовку для нового элемента Nest.

    Список команд будет расширяться по мере развития утилиты, поэтому не забывайте периодически обновлять её. Обратите внимание на вертикальную черту в некоторых командах, например, в команде new. После вертикальной черты указан псевдоним — дополнительное имя команды.

    Некоторые команды используются особенно часто. Чтобы сэкономить время на ввод полного имени команды, вы можете использовать псевдонимы. Обычно они состоят из одного или нескольких символов, что делает их быстрее для набора на клавиатуре.

    Давайте рассмотрим, как создать заготовку нового приложения, используя команду new:

    nest new my-awesome-project
    

    Эта команда создаст новую директорию с именем my-awesome-project и сгенерирует все необходимые файлы для вашего нового проекта.

    Чтобы сделать процесс еще проще, команда new имеет псевдоним — n. Это значит, что вместо того, чтобы вводить new, вы можете просто ввести n. Удобно, правда?

    Кроме команды new, есть и другие полезные псевдонимы для часто используемых команд:

    • new — n
    • info — i
    • generate — g

    На практике, команды new и generate используются чаще всего, поэтому обязательно запомните их псевдонимы!

    nest n my-awesome-project
    

    Обратите внимание на то, что описание команд содержит секцию [options]. Это означает, что существуют дополнительные параметры, которые вы можете использовать, но они не обязательны. Если вы видите квадратные скобки, знайте — это опции, которые можно пропустить.

    С другой стороны, всё, что заключено в угловые скобки, обязательно для ввода. Не забудьте об этом!

    Создание нового проекта с помощью Nest

    Команда new позволяет сгенерировать основу для нового проекта на Nest. Что именно включает в себя “основа”? Это начальная файловая структура проекта, установка основных зависимостей, подготовка конфигурационных файлов, инициализация git-репозитория и многое другое. Nest CLI делает все это за вас, чтобы вы могли сосредоточиться на разработке. Если вам нужно, вы всегда можете внести изменения и настроить отдельные части проекта по своему усмотрению.

    Команда new поддерживает два режима работы: стандартный (для самостоятельных проектов) и монорепозиторий (для проектов, использующих монорепозитории). В этом руководстве мы сосредоточимся на первом варианте, а позже обсудим, как работать с монорепозиториями.

    Практически все команды поддерживают параметр --dry-run (-d). Это невероятно полезный параметр, который позволяет вам увидеть, какие изменения будут внесены в файловую систему после выполнения команды без параметра --dry-run. Например, команда nest n my-awesome-project --dry-run выведет список файлов, которые будут созданы с помощью команды new. Этот параметр особенно полезен, когда вы изучаете Nest CLI.

    Не забывайте, что любые команды Nest CLI поддерживают параметр --dry-run (см. врезку). Давайте воспользуемся им с командой new, чтобы увидеть список файлов, которые будут сгенерированы после выполнения команды:

    nest new my-awesome-project --dry-run
    

    После выполнения команды будет создан список файлов, которые появятся, если запустить команду без параметра --dry-run:

    photo_2024-11-08_11-28-06.jpg

    Параметр --dry-run позволяет вам увидеть список файлов, которые будут созданы в результате выполнения команды. На втором рисунке вы можете заметить, что при создании проекта будут подготовлены файлы конфигурации для таких инструментов, как ESLint, Prettier и другие. Теперь давайте попробуем сгенерировать новый проект, повторив команду, но без параметра --dry-run:

    nest new my-awesome-project
    

    Когда вы запускаете команду без дополнительных параметров, активируется «мастер» подготовки нового проекта. На первом шаге вам будет предложено ответить на вопрос: “Какой менеджер пакетов вы будете использовать?” Доступны три варианта: npm, yarn и pnpm. В рамках серии статей мы будем использовать npm.

    После выбора менеджера пакетов начнётся процесс подготовки проекта: создадутся необходимые файлы, инициализируется git-репозиторий, установятся зависимости и многое другое. Это позволит вам быстро приступить к разработке вашего нового проекта!

    photo_2024-11-08_11-34-49.jpg

    photo_2024-11-08_11-34-47.jpg

    После создания основы проекта перейдите в папку с проектом. Чтобы запустить проект, используйте npm-скрипт start или start:dev (эти сценарии рассматриваются в отдельной статье).

    # Перейдём в папку с проектом
    cd my-awesome-project
    
    # Запустим проект в dev-режиме
    npm run start:dev
    

    photo_2024-11-08_11-36-49.jpg

    На четвёртом рисунке представлен пример запущенного проекта. Обратите внимание, что в проекте уже реализована поддержка логирования, создан тестовый маршрут и настроен watch-режим. Это значит, что при изменении файлов проект автоматически перезапускается, что значительно упрощает процесс разработки.

    Параметры для команды new

    Команда new поддерживает различные параметры (options). Мы уже рассмотрели один из них — --dry-run, но это не единственный доступный параметр. Вот список поддерживаемых опций:

    • --dry-run, -d: Выводит информацию о файлах, которые будут созданы в результате выполнения команды.
    • --skip-git, -g: Пропускает инициализацию git-репозитория.
    • --skip-install, -s: Пропускает установку зависимостей.
    • --package-manager: Позволяет определить менеджер пакетов. Доступные варианты: npm, yarn или pnpm.
    • --language: Устанавливает язык разработки: TS (TypeScript) или JS (JavaScript).
    • --strict: Включает строгий режим для TypeScript. В конфигурации ts активируются флаги: strictNullChecks, noImplicitAny, strictBindCallApply, forceConsistentCasingInFileNames, noFallthroughCasesInSwitch.

    С помощью этих параметров вы можете легко создавать дополнительные сценарии автоматизации. Например, если вам часто приходится создавать проекты с одинаковой конфигурацией, вы можете подготовить сценарий с перечислением необходимых опций. Таким образом, Nest CLI сформирует основу для проекта без лишних вопросов, что значительно сэкономит ваше время!

    Информация о проекте с помощью команды info

    Команда info выводит в терминал сводную информацию о проекте. Эта информация особенно полезна, когда вы присоединяетесь к существующему проекту, так как позволяет понять, какие версии компонентов Nest используются.

    Чтобы получить информацию о проекте, просто выполните команду nest info в терминале, находясь в директории вашего проекта. Вот пример выполнения команды для проекта, который мы подготовили в предыдущем разделе:

    photo_2024-11-08_11-38-15.jpg

    Такой вывод поможет вам быстро ознакомиться с основными компонентами и их версиями, что облегчит дальнейшую работу с проектом.

    Генератор заготовок файлов с помощью команды generate

    Команда generate — это инструмент, с которым вам предстоит взаимодействовать особенно часто. Она позволяет создавать заготовки различных файлов, которые служат компонентами при разработке проекта на Nest. Например, Nest-модуль является одним из таких компонентов. Вы можете создать новый модуль вручную в редакторе, но команда generate сделает это за вас, подготовив все необходимые файлы (модуль, файл для тестирования и так далее) и выполнив начальное подключение. Это особенно удобно на начальном этапе разработки.

    Синтаксис команды

    Синтаксис команды выглядит следующим образом:

    nest generate <schematic> <name> [options]
    Или с использованием псевдонима `g`:
    nest g <schematic> <name> [options]
    

    Команда generate ожидает два обязательных аргумента:

    • <schematic> — схема, для которой будет сгенерирована заготовка. Вы можете рассматривать схему как компонент для разработки приложения. Например, class (для формирования заготовки класса), controller (для формирования контроллера) и так далее. Список поддерживаемых схем представлен ниже.

    • <name> — имя компонента. В качестве имени компонента может использоваться любое валидное имя для директории или файла в файловой системе.

    Теперь давайте разберёмся с видами схем, для которых команда generate умеет создавать заготовки. Список поддерживаемых схем приведён в таблице.

    Схема Псевдоним Описание
    app Нет Создаёт новое Nest-приложение в монорепозитории.
    library lib Создаёт заготовку для новой библиотеки в монорепозитории.
    class cl Создаёт заготовку для нового класса.
    controller co Создаёт заготовку для контроллера.
    decorator d Создаёт заготовку для пользовательского декоратора.
    filter f Создаёт заготовку для filter.
    gateway ga Создаёт заготовку для gateway.
    guard gu Создаёт заготовку для guard.
    interface itf Создаёт заготовку для описания интерфейса.
    interceptor itc Создаёт заготовку для описания interceptor.
    middleware mi Создаёт заготовку для middleware.
    module mo Создаёт заготовку для нового модуля.
    pipe pi Создаёт новый pipe.
    provider pr Создаёт заготовку для provider.
    resolver r Создаёт заготовку для resolver.
    resource res Заготовка для CRUD (Create Read Update Delete) ресурса.
    service s Создаёт заготовку для сервиса.

    Как вы можете заметить, в Nest имеется множество готовых компонентов (абстракций). Мы будем знакомиться с ними по мере выхода статей по NestJs и по мере необходимости. А сейчас давайте посмотрим на пример выполнения команды generate. Обратите внимание, что команду следует выполнять в директории с вашим Nest-проектом:

    nest generate controller user
    

    В приведенной выше команде мы применили её к новому проекту на Nest. В результате была создана новая директория user, а в ней — два файла: user.controller.ts (заготовка для контроллера) и user.controller.spec.ts (заготовка для тестов контроллера).

    Теперь давайте заглянем внутрь файла контроллера:

    import { Controller } from '@nestjs/common';
    
    @Controller('user')
    export class UserController {}
    

    В этом файле был сформирован класс для будущего контроллера, к которому уже применён одноимённый декоратор (с деталями мы познакомимся позже). Кроме того, помимо создания новых файлов, был изменён основной модуль приложения (app.module.ts), в который добавлено подключение нового контроллера.

    photo_2024-11-08_11-47-27.jpg

    Заготовки для других компонентов создаются аналогичным образом. Может показаться, что Nest CLI не приносит особой пользы, так как перечисленные файлы и изменения легко можно сделать вручную. Однако Nest CLI открывает большие перспективы для автоматизации разработки. Рассмотрим ещё один пример: на этот раз мы сформируем заготовку сервиса user. Для краткости воспользуемся псевдонимами:

    Вместо длинных слов generate и service мы используем псевдонимы g и s. Эту команду мы снова выполнили на стартовом проекте, где в прошлый раз создавали контроллер. Результатом выполнения стали новые файлы: user.service.ts и user.service.spec.ts. Модуль приложения также обновился, и в него было добавлено подключение сервиса user.

    Важно отметить, что при запуске команды мы не указываем, где именно создать сервис. Выбор пути происходит на основании имени компонента. В нашем случае мы указали имя user, и заготовка сервиса автоматически была создана в директории users, рядом с ранее созданным контроллером.

    Параметры для команды generate

    Команда generate поддерживает различные параметры. Обратите внимание на параметр --dry-run, который особенно полезен в связке с командой generate:

    • --dry-run, -d: Выводит информацию о файлах, которые будут созданы в результате выполнения команды.
    • --project [project], -p: Указывает проект, в который следует добавить компонент.
    • --flat: Не создаёт директории для компонентов.
    • --spec: Генерирует заготовки для тестов. Эта опция применяется по умолчанию.
    • --no-spec: Не создаёт заготовки для тестов.

    Заключение

    Nest CLI — это мощный инструмент для командной строки, который упрощает работу с фреймворком Nest и позволяет автоматизировать некоторые типовые сценарии. Среди таких сценариев можно выделить: автоматическое формирование заготовок для различных компонентов, подготовку фундамента для проекта, управление проектами в монорепозитории и многое другое.

    В этом разделе мы познакомились с основными командами: new, generate, info. Эти команды являются наиболее часто используемыми при работе с Nest CLI. С другими командами мы познакомимся позднее.

    написал в NestJs
  • Введение в Nest: Путеводитель для новичков - 1

    Что такое Nest?

    Nest — это современный фреймворк для создания приложений на платформе Node.js. Если вы новичок в программировании, то представьте его как мощный набор инструментов, который упрощает разработку сложных приложений. Nest предоставляет готовые компоненты и абстракции, которые делают процесс создания приложений более удобным и эффективным. Раньше разработчикам приходилось самостоятельно реализовывать многие из этих функций, но теперь они доступны «из коробки»!

    Поддержка TypeScript и гибкость

    Nest полностью поддерживает язык TypeScript — это расширение JavaScript, которое добавляет типизацию и помогает избежать ошибок. Фреймворк поощряет объектно-ориентированный подход к программированию, но вы также можете использовать JavaScript, функциональный подход или другие привычные методы, которые вам нравятся.

    Зачем нужен Nest?

    Может показаться, что создать серьезное приложение можно и без Nest. Давайте рассмотрим, как это можно сделать. Вам потребуется:

    1. Установить актуальную версию Node.js.
    2. Настроить TypeScript — это поможет вам писать более безопасный и структурированный код.
    3. Установить Express.js — это библиотека для обработки HTTP-запросов, которая значительно упростит работу с веб-приложениями.
    4. Продумать архитектуру приложения — вам нужно будет создать абстракции для контроллеров, сервисов и логирования.

    Однако, если вы пойдете по этому пути, то столкнетесь с множеством абстракций и возникнет проблема внедрения зависимостей. Это может стать настоящей головной болью!

    Как Nest упрощает жизнь разработчикам?

    Nest решает проблему внедрения зависимостей с помощью встроенной поддержки. Вместо того чтобы настраивать сложные механизмы самостоятельно, вы можете воспользоваться мощными инструментами, которые уже включены в фреймворк. Например, библиотека Inversify поможет вам управлять зависимостями с помощью контейнера (IoC) и использовать паттерн Dependency Injection (DI).
    app-structures-min.jpg

    На самом деле, все перечисленные действия — это лишь верхушка айсберга. Когда вы начинаете разрабатывать приложение, скорее всего, вам потребуется работа с базами данных. Это значит, что нужно будет настраивать интеграцию с базой, создавать абстракции и так далее. То же самое касается и входных данных: обычно они передаются извне через переменные окружения, что требует использования дополнительных инструментов, таких как dotenv, convict и других.

    Логирование и его важность

    Не стоит забывать о логировании. Вряд ли найдется серьезное бэкенд-приложение без логов. Для Node.js существует множество библиотек для логирования. Вам нужно будет выбрать один из популярных вариантов и интегрировать его в проект, добавляя новые прослойки и абстракции. И вот снова алгоритм действий повторяется.

    Установить нужные пакеты — это несложно. Гораздо сложнее продумать, как их интегрировать в приложение, чтобы это не навредило масштабируемости. Также важно учитывать, что рано или поздно один из пакетов может понадобиться заменить на другой.

    Архитектура приложения

    Всё сводится к начальной архитектуре приложения. Если у вас есть опыт, вы сможете легко воспроизводить её от проекта к проекту. Но если опыта недостаточно, можно легко заблудиться, и неверные решения потребуют значительных переделок в будущем.

    Node.js и Express.js очень дружелюбны к разработчикам. Научиться создавать приложения и подключать готовые middleware легко, но выстраивание архитектуры может вызвать трудности. Эти инструменты не предоставляют готовой основы или инфраструктуры для вашего приложения.

    Это не обязательно минус: такая свобода — это гибкость. Однако только опытные разработчики смогут правильно её использовать. Продуманная архитектура и документация помогут новичкам легче интегрироваться в проект.

    Готовые решения для разработки

    Повторять одни и те же решения от проекта к проекту — не самая увлекательная задача. Здесь на помощь приходят фреймворки: они предлагают базовый набор компонентов, ограничения, абстракции и соглашения. Это позволяет сосредоточиться на проекте и не тратить много времени на реализацию базовых функций.

    Nest решает эту задачу. Он предоставляет готовые компоненты и абстракции, а также ряд правил и ограничений. Таким образом, разработчики получают готовый фундамент для своего приложения и документацию. Остается лишь разобраться с деталями и возможностями фреймворка, чтобы использовать их для создания приложения. Фокус на решении задач бизнеса становится приоритетом. Философия Nest заключается в упрощении создания масштабируемых приложений на Node.js и TypeScript.

    Преимущества Nest

    Nest универсален: он подходит для различных проектов. Создатели фреймворка часто подчеркивают его преимущества для больших приложений, требующих масштабируемости. Однако это не означает, что Nest избыточен для простых проектов. Нужен простой REST API сервис? Не проблема, Nest отлично с этим справится и даже поможет сгенерировать кодовые шаблоны. Если вам нужен что-то более сложное, например, GraphQL, Nest также будет полезен. Он подходит для создания CLI-приложений и микросервисов на Node.js — для всех этих задач Nest зарекомендовал себя очень хорошо.

    Еще одно неоспоримое преимущество — это готовые компоненты, абстракции и коннекторы, которые упрощают интеграцию с другими сервисами. Например, если вашему приложению требуется база данных, скорее всего, существуют пакеты, которые упростят интеграцию с Nest.

    Теперь давайте рассмотрим некоторые из инструментов, которые уже доступны «из коробки».

    Контроллеры в Nest

    Контроллеры — это готовая абстракция для обработки входящих запросов и формирования ответов для клиентов. Они позволяют организовать логику вашего приложения, принимая запросы и отправляя ответы.
    controllers-min.jpg

    Что делают контроллеры?

    Контроллеры могут обрабатывать несколько маршрутов, что дает вам возможность управлять различными запросами к вашему приложению. Например, вы можете создать один контроллер, который будет обрабатывать запросы на получение данных, добавление новых записей и обновление существующих.

    Интеграция с роутингом

    Контроллеры интегрированы со встроенным роутингом Nest, что упрощает работу с URL-адресами вашего приложения. Благодаря этому вам не нужно беспокоиться о том, как обрабатывать маршруты вручную — Nest делает это за вас.

    Декларативный подход

    С помощью декораторов, которые предоставляет Nest, вы можете выполнять часть действий декларативно. Это значит, что вы можете описывать маршруты и действия контроллеров более простым и понятным способом, что делает код более читаемым.

    Генерация контроллеров с помощью CLI

    Nest также предоставляет инструмент командной строки (CLI), который позволяет быстро генерировать основу для будущих контроллеров. Это избавляет вас от необходимости вручную создавать файлы и писать базовый код, что экономит время и усилия.

    Таким образом, контроллеры в Nest делают процесс обработки запросов простым и удобным, позволяя вам сосредоточиться на логике вашего приложения.

    Модули в Nest

    Модули — это важная часть Nest, которая помогает организовать структуру вашего приложения. Они позволяют группировать связанные задачи в одном месте.
    modules-min.jpg

    Что такое модуль?

    Модуль — это не просто файл, как может показаться. Это структурная единица, которая объединяет различные элементы, такие как сервисы, контроллеры и другие модули, которые решают определённые задачи.

    Пример: модуль «User»

    Например, модуль «User» может включать в себя:

    • Сервисы для работы с базой данных (например, для сохранения и извлечения информации о пользователях).
    • Сервисы с бизнес-логикой (например, для проверки прав доступа).
    • Контроллеры для обработки запросов, связанных с пользователями.

    Таким образом, модуль «User» охватывает всё, что связано с функциональностью, связанной с пользователями. Это делает код более организованным и понятным.

    Интерсепторы

    Интерсепторы — это абстракция, которая позволяет добавлять дополнительный код для изменения данных перед их передачей в контроллер или для добавления другой логики.

    Exception Filters

    В Nest есть встроенный слой для обработки необработанных исключений. Это значит, что если возникает ошибка, клиент получит корректный ответ (например, код состояния «500»). Для более детальной обработки можно создать дополнительные фильтры.

    Guards

    Guards — это компонент, который позволяет ограничить обработку запросов для определённых маршрутов. Например, они могут блокировать запросы от неавторизованных пользователей или от пользователей без необходимых прав.

    И другие возможности

    Nest универсален и хорошо задокументирован. Мы упомянули лишь некоторые функции, но их намного больше. Это позволяет ускорить разработку приложения и облегчить вход новых разработчиков в проект. Чтобы понять, как устроен проект, достаточно обратиться к документации.

    Минусы Nest

    Минусы Nest.js вытекают из его плюсов. Если ваша задача не совсем совпадает с концепцией Nest, могут возникнуть трудности. Это касается всех фреймворков: они предлагают готовую основу, но и накладывают ограничения. Если ваш проект подходит под эти ограничения, разработка будет гладкой. Если нет — придётся «перекраивать» некоторые части.

    Nest и Express

    Может показаться, что Nest изобретает уже существующие инструменты, но это не так. Nest — это в первую очередь абстракции и компоненты. Например, для обработки HTTP-запросов он использует Express.js. Это известный и проверенный инструмент.

    Nest скрывает Express и предоставляет свои интерфейсы для работы с ним. Это позволяет при необходимости заменить Express на другой фреймворк, например, fastify. Вы можете использовать либо его, либо Express. Замена фреймворка не требует полной переработки приложения благодаря абстракциям.

    Заключение

    Nest — универсальный фреймворк с готовыми абстракциями и компонентами. Он ускоряет разработку, так как разработчику не нужно тратить время на начальную настройку проекта. Кроме того, Nest хорошо документирован, что упрощает изучение и вход новых разработчиков в проект.

    написал в NestJs
  • RE: Проблема с дублированием данных при вызове return вместо reply.send - Fastify

    Кароче как оказалось проблема была в preHandler, у меня в роутинге к регистрации юзера была валидация body:

    fastifyInstance.post("/users/register", { preHandler: validateCreateUser }, createNewUser);
    

    В итоге я убрал preHandler, а логи валидации перенес в сервисы.

    написал в Fastify