Простыми словами, мы колбэчим submitHandler через debounceHandler то как оно должно быть по спецификации JS ? И почему onInput, а не onChange ?
kirilljs
@kirilljs
Фронтенд ковырялка
Лучшие сообщения kirilljs
-
RE: Оптимизация React js приложений. Использование функции debounde()
-
RE: Vue.js и React — необычное сравнение
@Jspi Ну вообще согласен полностью, vue по сути просто файлик в котором у тебя все тот же веб просто вместо html у тебя template и те же script, style. Соглы что вуй ближе к интернетам этим вашим. НО реакт - это как младший братик дурачек, а ты его все равно любишь
-
pm2 автоматический restart при перезагрузки сервера
Всем привет!
Если кто сталкивался вдруг с тем что ваши приложения в pm2 отваливаются при перезагрузки сервера, решение есть:Для начала необходимо синхронизировать настройки вашей системы pm2 и ваших приложений:
pm2 save
Если команда не срабатывается используйте:
pm2 save --force
Далее нам необходимо запустить скрипт startup, который как раз отвечает за автоматический запуск pm2 после перезагрузки сервера:
pm2 startup
После необходимо прописать скрипт:
sudo env PATH=$PATH:/usr/local/bin pm2 startup systemd -u [Имя пользователя] --hp /var/www/fastuser/data
На этом все! Теперь если ваш сервер перезагрузится - pm2 автоматически восстановит работы ваших приложений.
-
RE: Проверка стала проще с Zod: как обеспечить точность и качество форм
Vue говно, смысл на нем писать если все бигтехи пишут на Реакт?
Недавние сообщения kirilljs
-
Функции в 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?
- Экспортирование модулей: Позволяет делать функции, объекты или переменные доступными для других файлов. Используется
export
для экспорта нескольких элементов из модуля.
// utils.js export function add(a, b) { return a + b; } export const pi = 3.14159;
- Импортирование модулей: Позволяет использовать ранее экспортированные элементы. Используется
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
- Экспорт по умолчанию: Используется для экспорта одного основного элемента из модуля. Это может быть функция, класс или объект. Используется
export default
для этого.
// math.js export default function multiply(a, b) { return a * b; }
- Импорт по умолчанию: Осуществляется без фигурных скобок. Можно дать импортируемому элементу любое имя.
// main.js import multiply from './math.js'; console.log(multiply(2, 3)); // 6
- Динамический импорт: Позволяет загружать модули асинхронно, что полезно для загрузки модулей по требованию. Используется
import()
для динамического импорта.
// main.js async function loadModule() { const { add } = await import('./utils.js'); console.log(add(5, 10)); // 15 } loadModule();
- Импорт всего содержимого модуля:
Иногда нужно импортировать все содержимое модуля под одним именем. Используется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
- Экспортирование модулей: Позволяет делать функции, объекты или переменные доступными для других файлов. Используется
-
Свойство "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 на эту тему - ссылочка, хотя проблема старая, судя по всему один хер она почему-то нормально не фиксится.
Надеюсь это кому-то поможет
-
RE: Fastify - No Authorization was found in request.headers
Кароче решил проблему, все регистрировалось корректно - это я про регистрацию плагина и прочее.
Вообщем, я забывал передавать в заголовке сам токен когда проверял защищенные роуты
Проблема решена!)
-
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); } })();
-
Провайдеры в Nest JS - 1.3
Провайдеры (
providers
) — ключевая концепция фреймворка. Базовые классы, такие как сервисы (services
), фабрики (factories
) и репозитории (repositories
), могут быть провайдерами. Это одна из основных абстракций фреймворка Nest. Особенность провайдеров — возможность внедрения зависимостей. Это позволяет создавать сложные связи между объектами. Nest отвечает за создание экземпляров и передачу зависимостей.Провайдеры
Провайдер — это обычный класс (хотя не всегда так), который, например, может быть сервисом. Чтобы класс стал провайдером, его нужно пометить специальным декоратором и зарегистрировать.
Провайдеры на практике
Многие базовые классы в Nest могут быть провайдерами. Рассмотрим это на простом примере. Предположим, мы создаём приложение для доски объявлений, где пользователи могут размещать объявления о продаже ненужных вещей, а другие могут комментировать эти объявления. Пример архитектуры приложения представлен на следующем рисунке.
При разработке приложения логично разделить его на три модуля: объявления (
offers
), пользователи (users
) и комментарии (comments
). Каждый модуль будет иметь свой сервис для бизнес-логики и работы с базой данных.Представим задачу: удаление объявления должно автоматически удалять все связанные комментарии. Например, к объявлению с идентификатором
12
пользователи оставили100
комментариев. Эти комментарии хранятся в отдельной коллекции и управляются отдельным сервисом. Нам нужно реализовать метод в контроллереOfferController
, который будет удалять объявление и его комментарии.Одно из решений — использовать два сервиса (для объявлений и комментариев) в контроллере
OfferController
. Сервис комментариев предоставляет метод для удаления всех комментариев к определённому объявлению, а сервис объявлений — метод для удаления самого объявления. Таким образом, в контроллереOfferController
понадобятся оба сервиса для выполнения задачи.Схематично взаимодействие контроллера и сервисов показано на следующем рисунке. Контроллеру нужны два сервиса:
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
, следим за доступностью провайдера и внедряем зависимость через параметры конструктора.Если следовать интерфейсу
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. - Применить декоратор
-
Модули в Nest JS - 1.2
Модуль — это важная часть приложения на Nest. Каждое приложение начинается с корневого модуля, который Nest использует для создания структуры приложения. Эта структура помогает управлять зависимостями между модулями и провайдерами.
На практике это проще, чем кажется. Модули позволяют разработчикам разбивать приложение на отдельные части. Представьте, что каждый модуль — это компонент вашего приложения. Например, один модуль может отвечать за управление заказами в интернет-магазине, а другой — за управление пользователями. Вместе эти компоненты создают функциональное приложение.
Важно помнить, что модули в Nest не обязательно должны быть отдельными файлами. Это всего лишь абстракция, которая помогает структурировать приложение. Некоторые модули могут быть большими, другие — маленькими. Используя паттерн «композиция», разработчики могут создавать сложные модули из простых.
Хотя Nest позволяет создать приложение с одним модулем, это подходит только для очень простых проектов. С увеличением сложности приложения становится очевидным, что использование нескольких модулей — более эффективный подход.
Примечание: Абстракция «Модуль» в Nest реализует одноимённый структурный паттерн проектирования. Модуль объединяет данные, функции и физические компоненты в одну сущность, а взаимодействовать с ней можно через публичный интерфейс.
Как работать с модулями в Nest
В Nest модуль — это класс, помеченный декоратором @Module. Этот декоратор содержит метаданные, которые Nest использует для организации приложения.
Декоратор @Module принимает объект, который описывает структуру модуля. В этом объекте можно указать провайдеры и контроллеры модуля, а также модули, от которых он зависит, и провайдеры, которые он экспортирует. Вот основные свойства:
Свойство объекта Описание providers
Список провайдеров модуля. controllers
Список контроллеров модуля. imports
Список импортируемых модулей. exports
Список экспортируемых провайдеров модуля. Особое внимание стоит уделить свойствам imports и exports.
-
imports
: здесь указываются модули, которые нужны текущему модулю. Если модуль использует функциональность других модулей, их нужно перечислить в этом свойстве. Импортированные модули могут использовать экспортируемые провайдеры. -
exports
: в этом свойстве перечисляются провайдеры, которые будут доступны другим модулям при импорте. Это позволяет делиться функциональностью между модулями.
По умолчанию все провайдеры модуля скрыты. Чтобы использовать провайдер в другом модуле, его нужно экспортировать через свойство
exports
. Таким образом, экспортируемые провайдеры можно считать публичным интерфейсом модуля.Модули на практике
Чтобы понять, как работают модули в Nest, рассмотрим пример простого приложения — API для управления пользователями. В этом приложении нам понадобятся функции для запуска сервера и управления пользователями.
Для REST API создадим контроллер
UserController
, который будет обрабатывать маршруты. Если у нас есть бизнес-логика и взаимодействие с базой данных, нам понадобится модульUserService
, который будет реализовывать эту логику. Также потребуется описание сущности «Пользователь», например,UserEntity
.Каждый из этих элементов будет храниться в отдельном файле, что делает структуру проекта более удобной и понятной. Примерная схема файловой структуры может выглядеть следующим образом:
Давайте рассмотрим, что общего у контроллера, сервиса и сущности — все они относятся к одной области, связанной с управлением пользователями. Такие области называются доменами.
Примечание: Домен — это предметная область, которая включает бизнес-логику, процессы, данные и терминологию, связанные с этой областью.
Поскольку все эти элементы связаны, имеет смысл объединить их в функциональный модуль. Модуль «Пользователи» будет отвечать за работу с пользователями и предоставит публичный интерфейс для взаимодействия с другими модулями. Разработчик сможет использовать функциональность модуля, не углубляясь в детали его реализации.
С ростом приложения появятся и другие модули, например, для управления заказами или отправкой уведомлений. Разделение на функциональные модули упростит поддержку и установит четкие границы для каждого из них.
Примечание: 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
. Мы рассмотрим это на практике позже.Примечание: Хороший модуль должен решать одну конкретную задачу. Не стоит объединять всю функциональность приложения в одном модуле. Лучше разбивать на несколько, минимизируя зависимости между модулями.
Для каждого модуля обычно создаётся отдельная директория, где размещаются все его компоненты: сервисы, контроллеры и вспомогательные скрипты. Это позволяет удобно организовать код и иметь всё, что связано с модулем, в одном месте.
Подключение модуля
Чтобы использовать функциональность модуля, его нужно подключить в другом модуле. Как уже упоминалось, любое приложение на 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-приложение состоит минимум из одного модуля, но не стоит складывать всю функциональность в один модуль. Разделение на модули упрощает поддержку и выделяет функциональность в отдельные компоненты.
-
-
Nest CLI руководство- 1.1
Для удобной работы с Nest есть дополнительный инструмент — Nest CLI. Это приложение для командной строки, которое упрощает использование фреймворка и помогает автоматизировать часто выполняемые задачи. Например, с его помощью вы можете:
- Сгенерировать основу для проекта.
- Подготовить шаблоны для контроллеров, моделей и других компонентов.
Проще говоря, Nest CLI создаёт необходимые файлы и, в некоторых случаях, подключает их к модулю приложения.
Установка
Nest CLI — это обычный npm-пакет. Чтобы начать его использовать, нужно его установить. Есть несколько способов это сделать, но самый простой — установить пакет глобально. Для этого выполните команду:
npm install -g @nestjs/cli
После выполнения этой команды Nest CLI будет доступен для использования. Чтобы проверить, что установка прошла успешно, введите в терминале:
nest --help
Если всё установлено правильно, вы увидите список доступных команд:
Помимо глобальной установки, вы можете использовать утилиту 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
:Параметр
--dry-run
позволяет вам увидеть список файлов, которые будут созданы в результате выполнения команды. На втором рисунке вы можете заметить, что при создании проекта будут подготовлены файлы конфигурации для таких инструментов, как ESLint, Prettier и другие. Теперь давайте попробуем сгенерировать новый проект, повторив команду, но без параметра--dry-run
:nest new my-awesome-project
Когда вы запускаете команду без дополнительных параметров, активируется «мастер» подготовки нового проекта. На первом шаге вам будет предложено ответить на вопрос: “Какой менеджер пакетов вы будете использовать?” Доступны три варианта:
npm
,yarn
иpnpm
. В рамках серии статей мы будем использоватьnpm
.После выбора менеджера пакетов начнётся процесс подготовки проекта: создадутся необходимые файлы, инициализируется git-репозиторий, установятся зависимости и многое другое. Это позволит вам быстро приступить к разработке вашего нового проекта!
После создания основы проекта перейдите в папку с проектом. Чтобы запустить проект, используйте npm-скрипт
start
илиstart:dev
(эти сценарии рассматриваются в отдельной статье).# Перейдём в папку с проектом cd my-awesome-project # Запустим проект в dev-режиме npm run start:dev
На четвёртом рисунке представлен пример запущенного проекта. Обратите внимание, что в проекте уже реализована поддержка логирования, создан тестовый маршрут и настроен 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
в терминале, находясь в директории вашего проекта. Вот пример выполнения команды для проекта, который мы подготовили в предыдущем разделе:Такой вывод поможет вам быстро ознакомиться с основными компонентами и их версиями, что облегчит дальнейшую работу с проектом.
Генератор заготовок файлов с помощью команды
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), в который добавлено подключение нового контроллера.
Заготовки для других компонентов создаются аналогичным образом. Может показаться, что 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. С другими командами мы познакомимся позднее. -
Введение в Nest: Путеводитель для новичков - 1
Что такое Nest?
Nest — это современный фреймворк для создания приложений на платформе Node.js. Если вы новичок в программировании, то представьте его как мощный набор инструментов, который упрощает разработку сложных приложений. Nest предоставляет готовые компоненты и абстракции, которые делают процесс создания приложений более удобным и эффективным. Раньше разработчикам приходилось самостоятельно реализовывать многие из этих функций, но теперь они доступны «из коробки»!
Поддержка TypeScript и гибкость
Nest полностью поддерживает язык TypeScript — это расширение JavaScript, которое добавляет типизацию и помогает избежать ошибок. Фреймворк поощряет объектно-ориентированный подход к программированию, но вы также можете использовать JavaScript, функциональный подход или другие привычные методы, которые вам нравятся.
Зачем нужен Nest?
Может показаться, что создать серьезное приложение можно и без Nest. Давайте рассмотрим, как это можно сделать. Вам потребуется:
- Установить актуальную версию Node.js.
- Настроить TypeScript — это поможет вам писать более безопасный и структурированный код.
- Установить Express.js — это библиотека для обработки HTTP-запросов, которая значительно упростит работу с веб-приложениями.
- Продумать архитектуру приложения — вам нужно будет создать абстракции для контроллеров, сервисов и логирования.
Однако, если вы пойдете по этому пути, то столкнетесь с множеством абстракций и возникнет проблема внедрения зависимостей. Это может стать настоящей головной болью!
Как Nest упрощает жизнь разработчикам?
Nest решает проблему внедрения зависимостей с помощью встроенной поддержки. Вместо того чтобы настраивать сложные механизмы самостоятельно, вы можете воспользоваться мощными инструментами, которые уже включены в фреймворк. Например, библиотека Inversify поможет вам управлять зависимостями с помощью контейнера (IoC) и использовать паттерн Dependency Injection (DI).
На самом деле, все перечисленные действия — это лишь верхушка айсберга. Когда вы начинаете разрабатывать приложение, скорее всего, вам потребуется работа с базами данных. Это значит, что нужно будет настраивать интеграцию с базой, создавать абстракции и так далее. То же самое касается и входных данных: обычно они передаются извне через переменные окружения, что требует использования дополнительных инструментов, таких как
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
Контроллеры — это готовая абстракция для обработки входящих запросов и формирования ответов для клиентов. Они позволяют организовать логику вашего приложения, принимая запросы и отправляя ответы.
Что делают контроллеры?
Контроллеры могут обрабатывать несколько маршрутов, что дает вам возможность управлять различными запросами к вашему приложению. Например, вы можете создать один контроллер, который будет обрабатывать запросы на получение данных, добавление новых записей и обновление существующих.
Интеграция с роутингом
Контроллеры интегрированы со встроенным роутингом Nest, что упрощает работу с URL-адресами вашего приложения. Благодаря этому вам не нужно беспокоиться о том, как обрабатывать маршруты вручную — Nest делает это за вас.
Декларативный подход
С помощью декораторов, которые предоставляет Nest, вы можете выполнять часть действий декларативно. Это значит, что вы можете описывать маршруты и действия контроллеров более простым и понятным способом, что делает код более читаемым.
Генерация контроллеров с помощью CLI
Nest также предоставляет инструмент командной строки (CLI), который позволяет быстро генерировать основу для будущих контроллеров. Это избавляет вас от необходимости вручную создавать файлы и писать базовый код, что экономит время и усилия.
Таким образом, контроллеры в Nest делают процесс обработки запросов простым и удобным, позволяя вам сосредоточиться на логике вашего приложения.
Модули в Nest
Модули — это важная часть Nest, которая помогает организовать структуру вашего приложения. Они позволяют группировать связанные задачи в одном месте.
Что такое модуль?
Модуль — это не просто файл, как может показаться. Это структурная единица, которая объединяет различные элементы, такие как сервисы, контроллеры и другие модули, которые решают определённые задачи.
Пример: модуль «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 хорошо документирован, что упрощает изучение и вход новых разработчиков в проект.
-
RE: Проблема с дублированием данных при вызове return вместо reply.send - Fastify
Кароче как оказалось проблема была в preHandler, у меня в роутинге к регистрации юзера была валидация body:
fastifyInstance.post("/users/register", { preHandler: validateCreateUser }, createNewUser);
В итоге я убрал preHandler, а логи валидации перенес в сервисы.