Веб-фреймворк Express (Node.js/JavaScript)
Express представляет собой популярный веб-фреймворк, написанный на JavaScript и работающий внутри среды исполнения node.js. Этот модуль освещает некоторые ключевые преимущества этого фреймворка, установку среды разработки и выполнение основных задач веб-разработки и развёртывания.
Предварительные требования
Перед началом этого модуля вам необходимо представлять, что из себя представляет серверное программирование и веб-фреймворки, желательно из прочтения статей другого модуля Server-side website programming first steps. Знакомство с основными концепциями программирования и языком программирования JavaScript будет очень полезным, но оно не является обязательным для понимания базовых понятий этого модуля.
Примечание: Этот веб-сайт содержит множество источников для изучения JavaScript в контексте разработки на стороне клиента: JavaScript, JavaScript Guide, JavaScript Basics, JavaScript (изучение). Ключевые особенности и концепции языка JavaScript остаются сходными и для серверной разработки на Node.js и используемый материал достаточно релевантен. Node.js предоставляет additional APIs для обеспечения функциональности, которая полезна для «безбраузерной» разработки, т.е. для создания HTTP-сервера и доступа к файловой системе, но не поддерживает JavaScript APIs для работы с браузером и DOM.
Это руководство обеспечит вас некоторой информацией о работе с Node.js и Express, но также существуют и другие многочисленные отличные ресурсы в Интернете и книгах — некоторые из них доступны из тем How do I get started with Node.js (StackOverflow) и What are the best resources for learning Node.js? (Quora).
Руководства
В первой статье об Express мы ответим на вопросы «Что такое Node?» и «Что такое Express?» и дадим вам представление о том, что делает веб-фреймворк Express особенным. Мы расскажем об основных функциях и покажем вам некоторые из основных строительных блоков приложений Express (хотя на данный момент у вас ещё нет среды разработки, в которой можно её протестировать).
Теперь, когда вы знаете, что такое Express, мы покажем вам, как настроить и протестировать среду разработки Node/Express в Windows, Linux (Ubuntu) и Mac OS X. Независимо от того, какую популярную операционную систему вы используете, эта статья даст вам то, что вам нужно, чтобы начать разработку приложений Express.
Первая статья в нашей серии практических уроков объясняет, что вы будете изучать, и предоставит обзор веб-сайта «локальной библиотеки», над которым мы будем работать и развивать в последующих статьях.
В этой статье показано, как вы можете создать «скелет» веб-сайта, который затем можно будет заполнить с помощью маршрутов сайта, шаблонов/представлений и баз данных.
В этой статье кратко представлены базы данных для Node/Express. Затем показывается, как мы можем использовать Mongoose для обеспечения доступа к баз данных для сайта LocalLibrary. В уроке объясняется, как объявляются объектная схема и модели, основные типы полей и базовая валидация. Также кратко показаны некоторые из основных способов доступа к данным модели.
В этом уроке мы создадим маршруты (код обработки URL) с «фиктивным» обработчиком функций для всех конечных точек ресурсов, которые нам в конечном итоге понадобятся для сайта LocalLibrary. По завершении мы будем иметь модульную структуру нашего кода обработки маршрута, который мы можем расширить с помощью функций реального обработчика в следующих статьях. Мы также будем очень хорошо понимать, как создавать модульные маршруты, используя Express.
Теперь мы готовы добавить страницы, на которых будут отображаться книги веб-сайта LocalLibrary и другие данные. Страницы будут включать главную страницу, которая показывает сколько записей определённого типа мы имеем и отдельную страницу для детального просмотра записи. По пути мы получим практический опыт в получении записей из баз данных и использовании шаблонов.
В этой части мы покажем вам, как работать с HTML формами в Express, используя Pug, и в частности, как создавать, обновлять и удалять документы из базы данных.
Теперь когда вы создали восхитительный сайт LocalLibrary, вы захотите установить его на общедоступном сервере, чтобы он мог дать доступ персоналу библиотеки и пользователям в Интернет. В этой статье представлен обзор того, как вы можете найти хост для развёртывания вашего сайта и что вам нужно сделать, чтобы подготовить ваш сайт к публикации.
Смотрите также
В этой статье представлена практическая демонстрация того, как установить LocalLibrary на облаке Pivotal Web Services PaaS — это полнофункциональная альтернатива с открытым исходным кодом для Heroku, облачного сервиса PaaS используемого в части 7 этого учебника, представленного выше. PWS/Cloud Foundry определённо стоит попробовать, если вы ищете альтернативу Heroku (или другому PaaS облачному сервису), или просто хотите попробовать что-то другое.
Изучите другие учебники
Это все статьи учебника (на данный момент). Если вы хотите продолжить обучение, есть другие интересные темы:
- Использование сессий
- Авторизация пользователей
- Авторизация пользователей и уровни доступа
- Тестирование веб приложений Express
- Веб безопасность для веб приложений Express.
И, конечно, было бы неплохо создать какой-либо проверочный тест знаний!
Found a content problem with this page?
- Edit the page on GitHub.
- Report the content issue.
- View the source on GitHub.
This page was last modified on 3 авг. 2023 г. by MDN contributors.
Your blueprint for a better internet.
Как настроить Node.js Express сервер для React
React — это библиотека JavaScript для разработки пользовательских интерфейсов. Она позволяет создавать эффективные и масштабируемые веб-приложения, основанные на компонентной архитектуре.
Express.js — это минималистичный и гибкий веб-фреймворк для Node.js, который облегчает разработку веб-приложений и API. Он предоставляет простой интерфейс и набор функций, позволяющих быстро создавать серверы и маршрутизировать запросы.
Введение
Это руководство поможет вам разработать простое приложение на React и подключить его к серверу, созданному с использованием Node.js. Мы начнем с создания React приложения с помощью команды create-react-app , а затем настроим его подключение к серверу Node.js c помощью proxy .
Необходимые условия
Для успешного выполнения данного гайда будет полезно иметь следующее:
- Предварительный опыт работы с Node.js, Express, npm и React.js.
- Установленный Node.js.
- Текстовый редактор, предпочтительно VS Code.
- Веб-браузер, в данном случае Google Chrome.
Настройка структуры папок
Первым шагом будет создание корневой папки для нашего приложения с именем express-react-app , в котором будут содержаться все файлы приложения. Затем мы создадим папку client , которая будет содержать все файлы React приложения.
Папка node_modules будет содержать все пакеты NPM для файла server.js . Папка node_modules будет автоматически создана при установке пакетов NPM.
Далее нам потребуется создать файл server.js . В этом файле будет размещен сервер Express, который будет выступать в качестве нашего бэкенда. Файл package.json будет автоматически сгенерирован, когда в терминале будет выполнена команда npm init -y .
Создание React приложения
Из терминала перейдите в корневую директорию с помощью команды cd и выполните следующие команды:
$cd express-react-app $npx create-react-app client
Вышеуказанные команды создадут React приложение названием client внутри корневой директории.
Настройка сервера Express
Следующий шаг состоит в создании сервера Express в файле server.js .
Из терминала перейдите в корневую директорию и выполните следующую команду:
$npm init -y
Команда автоматически сгенерирует файл package.json . Затем нам потребуется выполнить следующую команду для установки Express, и она будет сохранена в качестве зависимости в файле package.json .
$npm install express --save
Теперь отредактируйте файл server.js следующим образом:
const express = require('express'); //Строка 1 const app = express(); //Строка 2 const port = process.env.PORT || 5000; //Строка 3 // Сообщение о том, что сервер запущен и прослушивает указанный порт app.listen(port, () => console.log(`Listening on port $`)); //Строка 6 // Создание GET маршрута app.get('/express_backend', (req, res) => < //Строка 9 res.send(< express: 'YOUR EXPRESS BACKEND IS CONNECTED TO REACT' >); //Строка 10 >); //Строка 11
Строки 1 и 2 — подключают модуль Express и позволяют использовать его внутри файла server.js .
Строка 3 — Установка порта, на котором будет работать сервер Express.
Строка 6 — будет отображено сообщение в консоли о том, что сервер работает исправно.
Строка 9 и 11 — установка GET маршрута, который позже мы будем получать из нашего клиентского React приложения.
Настройка proxy
На этом шаге Webpack Development Server был автоматически сгенерирован при выполнении команды create-react-app . Наше React приложение работает на Webpack Development Server на стороне фронденда.
Webpack Development Server (WDS) — это инструмент, который помогает разработчикам вносить изменения во фронтенд веб-приложения и автоматически отображает эти изменения в браузере без необходимости обновления страницы.
Он уникален по сравнению с другими инструментами, которые делают то же самое, поскольку содержимое пакета не записывается на диск в виде файлов, а хранится в памяти. Это преимущество крайне важно при отладке кода и стилей.
Мы можем настроить проксирование запросов API с клиентской стороны на API на серверной стороне. API на серверной стороне (Express сервер) будет работать на порту 5000.
Сначала настройте прокси для перехода в директорию client и найдите файл package.json . Добавьте следующую строку в него.
“proxy”: “http://localhost:5000”
Измененный файл package.json будет выглядеть следующим образом:
< "name": "client", "version": "0.1.0", "private": true, "dependencies": < "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" >, "scripts": < "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" >, "eslintConfig": < "extends": [ "react-app", "react-app/jest" ] >, "browserslist": < "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] >, "proxy": "http://localhost:5000" >
Измененный файл package.json позволит Webpack проксировать запросы API на сервер бэкенда Express, работающий на порту 5000.
Вызов бэкенд сервера Express из React
Сначала перейдите в папку client/src и отредактируйте файл App.js , чтобы он выглядел следующим образом:
import React, < useEffect, useState >from 'react'; import logo from './logo.svg'; import './App.css'; function App() < const [state, setState] = useState(null); const callBackendAPI = async () => < const response = await fetch('/express_backend'); const body = await response.json(); if (response.status !== 200) < throw Error(body.message) >return body; >; // получение GET маршрута с сервера Express, который соответствует GET из server.js useEffect(() => < callBackendAPI() .then(res =>setState(res.express)) .catch(err => console.log(err)); >, []) return (
className="App-logo" alt="logo" /> Edit src/App.js and save to reload.
Learn React * вывод данных, полученных с сервера Express */> ); > export default App;
Внутри хука useEffect() вызывается функция callBackendAPI() . Эта функция будет получать данные с ранее созданного маршрута на сервере Express. При получении ответа от запроса fetch , значение res.express устанавливается в состояние state с помощью функции setState() . Затем значение state выводится внутри элемента для отображения на странице.
Запуск приложения
Для запуска приложения перейдите в корневую директорию express-react-app и выполните следующую команду:
$cd express-react-app $node server.js
После запуска файла server.js следующим шагом будет переход в веб-браузер по адресу «http://localhost:5000/express_backend«, и будет отображено следующее сообщение:

Вышеуказанное демонстрирует, что наш сервер Express работает должным образом, и созданный нами маршрут GET функционирует, а также возможно получение этого маршрута с клиентской стороны.
Также обратите внимание, что путь URL совпадает с путем, который мы указали в нашем маршруте GET в файле server.js .
Затем перейдите в директорию client в терминале и выполните следующие команды:
$cd client $npm start
Вышеуказанные команды запустят React сервер разработки, который работает на порту 3000, и автоматически откроется в веб-браузере.
На экране будет отображено следующее сообщение:

Наконец, мы отобразили данные, полученные с маршрута GET в server.js , в нашем фронтенд React приложении, как было показано выше.
Если сервер Express отключен, сервер React всё равно будет продолжать работать. Однако связь с бэкендом будет потеряна, и ничего не будет отображаться.
Заключение
С помощью сервера Express можно сделать многое, например, осуществлять вызовы к базе данных. Однако в этом руководстве мы сосредоточились на том, как быстро подключить клиентское React приложение к серверу Express на стороне бэкенда.
Кодовые фрагменты и файлы, использованные в этом руководстве, можно найти в репозитории GitHub по этой ссылке.
Использование CORS в Node.js


Изучение
На чтение 5 мин Просмотров 5.2к. Опубликовано 10.03.2022
Слово CORS означает «Совместное использование ресурсов между источниками». Cross-Origin Resource Sharing — это механизм, основанный на HTTP-заголовке, реализованный браузером, который позволяет серверу или API (интерфейс прикладного программирования) указывать любые источники (отличающиеся с точки зрения протокола, имени хоста или порта), отличные от источника, из которого неизвестный источник получает разрешение на доступ и загрузку ресурсов. Пакет cors, доступный в реестре npm, используется для устранения ошибок CORS в приложении Node.js.
Давайте разберемся на примере.
Шаг 1: Установка
Поскольку пакет CORS доступен в npm (диспетчере пакетов узла), который является сторонним пакетом Node.js, мы должны установить Node.js в нашей локальной системе. Для проверки введите следующую команду в терминале.
node -v
Команда покажет версию Node.js, установленную в вашей системе. Если выдает какую-то ошибку, заставьте правильно установить Node.js.
Шаг 2: Настройка проекта и структура папок. Сначала создайте в своей системе папку с именем «geeksforgeeks» и перейдите в нее с помощью командной строки. Для этого используйте следующую команду.
mkdir geeksforgeeks && cd geeksforgeeks
Внутри этой папки создайте две отдельные папки: клиентскую и серверную ( используя ту же команду mkdir ). Также внутри клиента папка создает файлы index.html и srcipt.js. Внутри папки сервера введите следующую команду для создания файла package.json:
npm init
Теперь создайте файл index.js в папке сервера, чтобы написать логику на стороне сервера. Наша текущая структура папок должна выглядеть так.
Структура проекта: Это будет выглядеть следующим образом.

Шаг 3: Теперь в том же каталоге установите необходимые пакеты (express и cors), используя следующую команду:
npm install express cors
Шаг 4: Это код внутри файла index.html. Это основной HTML-код, который будет показан клиенту в браузере.
< meta name = "viewport"
content = "width=device-width, initial-scale=1.0" >
< title >Sample webpage title >
Шаг 5: Это код внутри файла script.js. Файл script.js будет использоваться для получения данных с сервера для заполнения внешнего интерфейса.
// script.js file // Fetching data from server running at port 5000. fetch('http://localhost:5000/secret') .then((res) => res.json()) .then((data) => console.log(data));
Шаг 6: Клиентская часть работает на порту 5500. Теперь давайте посмотрим на проблему без ошибок. Теперь давайте создадим сервер Express в index.js без пакета cors, чтобы показать ошибку CORS.
// index.js file // Requiring module const express = require('express') const cors = require('cors') // Creating express app const app = express() // Sample api routes for testing app.get('/',(req, res) => < res.json("welcome to our server") >); app.get('/secret',(req, res) => < const secret = Math.floor(Math.random()*100) res.json() >); // Port Number const port = 5000; // Server setup app.listen(port, () => < console.log(`Server running on port $.`) >);
Шаг для запуска приложения : Запустите сервер, введя следующую команду:
node index.js
Вывод: это приведет к ошибке CORS, и желаемые ресурсы не будут получены из конечных точек API.

Шаг 7: Включение CORS для всех маршрутов сервера, новый index.js понравится.
//index.js file // Requiring module const express = require('express') const cors = require('cors') // Creating express app const app = express() // Enabling CORS for any unknown // origin(https://xyz.example.com) app.use(cors()); // Sample api routes for testing app.get('/',(req, res) => < res.json("welcome to our server") >); app.get('/secret',(req, res) => < const secret = Math.floor(Math.random()*100) res.json() >); // Port Number const port = 5000; // Server setup app.listen(port, () => < console.log(`Server running on port $.`) >);
Мы должны использовать дополнительные две строки, чтобы включить CORS для всех маршрутов.
const cors = require(cors) app.use(cors()) # this line enables CORS action for all routes as no argument has been passed.
Вывод: это приведет к желаемому результату, как мы видим ниже.

Шаг 8: Теперь давайте создадим CORS для указанного маршрута. Но может быть случай, когда только определенное количество маршрутов на нашем сервере должно быть разрешено обслуживать ресурсы для любых источников XYZ, и нам необходимо защитить другие маршруты от потенциального злоумышленника (любой URL-адрес, не соответствующий политике того же происхождения) из-за к конфиденциальности. Здесь мы включим запросы CORS для определенного количества маршрутов, как показано ниже.
// Requiring module const express = require('express') const cors = require('cors') // Creating express app const app = express() // Enabling CORS for some specific origins only. let corsOptions = < origin : ['http://localhost:5500'], >app.use(cors(corsOptions)) // Sample api routes for testing app.get('/',(req, res) => < res.json("welcome to our server") >); app.get('/secret', cors(corsOptions) , (req, res) => < const secret = Math.floor(Math.random()*100) res.json() >); // Port Number const port = 5000; // Server setup app.listen(port, () => < console.log(`Server running on port $.`) >);
Мы разрешаем запросы из определенных источников с помощью объекта corsOptions.
let corsOptions = < origin : ['http://localhost:5500'], ># this corsOptions object enables CORS action for all origins running on port 5500 only. # So if an application is running on any port other than 5000(own origin) and 5500, no CORS action will be enabled.
Выход:
Руководство по обработке ошибок в Node.js
Ошибки есть в каждом коде. Мы перевели гайд разработчика Айо Исайя, в котором он рассказывает о системе ошибок и о том, как их устранять.
Раз вы читаете эту статью, вы, конечно, знакомы с концепцией ошибок в программировании. Это ошибки в коде, они же баги, которые приводят к сбою или неожиданному поведению программы. В отличие от некоторых языков, таких как Go и Rust, где вы вынуждены взаимодействовать с потенциальными ошибками на каждом этапе пути, в JavaScript и Node.js можно обойтись без согласованной стратегии обработки ошибок.
Однако именно такая стратегия делает жизнь проще. Цель статьи — познакомить вас с этими шаблонами для создания, доставки и обработки потенциальных ошибок. Шаблоны помогут обнаружить и обработать потенциальные ошибки в коде до развёртывания.
Что такое ошибки в Node.js
Ошибка в Node.js — это любой экземпляр объекта Error. Общие примеры включают встроенные классы ошибок: ReferenceError , RangeError , TypeError , URIError , EvalError и SyntaxError . Пользовательские ошибки также можно создать путём расширения базового объекта Error, встроенного класса ошибки или другой настраиваемой ошибки. При создании ошибок таким путём нужно передать строку сообщения, описывающую ошибку. К сообщению можно получить доступ через свойство message объекта. Объект Error также содержит свойства name и stack , которые указывают имя ошибки и точку в коде, в которой объект создаётся.
const userError = new TypeError("Something happened!"); console.log(userError.name); // TypeError console.log(userError.message); // Something happened! console.log(userError.stack); /*TypeError: Something happened! at Object. (/home/ayo/dev/demo/main.js:2:19) at node:internal/main/run_main_module:17:47 */
Функции объекта Error можно передать или вернуть из функции. Если бросить его с помощью throw , объект Error станет исключением. Когда вы передаёте ошибку из функции, она переходит вверх по стеку, пока исключение не будет поймано. В противном случае uncaught exception может обвалить всю работу.
Как обработать ошибку
Оптимальный способ обработки ошибок функции JavaScript зависит от того, выполняет ли эта функция синхронную или асинхронную операцию. Рассмотрим четыре общих шаблона, позволяющих обрабатывать ошибки функций в Node.js.
Исключения
Чаще всего ошибки функций обрабатывают путём генерации. В этом случае ошибка становится исключением, после чего её можно поймать где-нибудь в стеке с помощью блока try / catch . Если у ошибки есть разрешение всплывать в стеке, не будучи перехваченной, она преобразуется в формат uncaughtException , что приводит к преждевременному завершению работы приложения. Например, встроенный метод JSON.parse () выдаёт ошибку, если строковый аргумент не является допустимым объектом JSON.
function parseJSON(data) < return JSON.parse(data); >try < const result = parseJSON('A string'); >catch (err) < console.log(err.message); // Unexpected token A in JSON at position 0 >
Для использования этого шаблона в функциях нужно добавить ключевое слово throw перед экземпляром ошибки. Этот шаблон сообщения об ошибках и обработки идиоматичен для функций, выполняющих синхронные операции.
function square(num) < if (typeof num !== 'number') < throw new TypeError(`Expected number but got: $`); > return num * num; > try < square('8'); >catch (err) < console.log(err.message); // Expected number but got: string >
Колбэк с первым аргументом-ошибкой
Из-за своей асинхронной природы Node.js интенсивно использует функции колбэка для обработки большей части ошибок. Колбэк (обратный вызов) передаётся в качестве аргумента другой функции и выполняется, когда последняя завершает свою работу.
Node.js использует колбэк с первым аргументом-ошибкой в большинстве асинхронных методов, чтобы гарантировать проверку ошибок до результатов операции. Колбэк обычно является последним аргументом функции, инициирующей асинхронную операцию, и вызывается один раз при возникновении ошибки или получении результата:
function (err, result) <>
Первый аргумент зарезервирован для объекта ошибки. Если ошибка возникает в ходе асинхронной операции, она доступна через аргумент err при неопределённом результате. Однако, если ошибки не возникает, err будет иметь значение null или undefined , а result будет содержать ожидаемый результат операции. Этот шаблон работает, если прочитать содержимое файла с помощью встроенного метода fs.readFile ():
const fs = require('fs'); fs.readFile('/path/to/file.txt', (err, result) => < if (err) < console.error(err); return; >// Log the file contents if no error console.log(result); >);
Метод readFile () использует колбэк в качестве своего последнего аргумента, который, в свою очередь, соответствует подписи функции «первая ошибка». В этом сценарии result включает в себя содержимое файла, который читается, если ошибки не возникает. В противном случае он определяется как undefined , а аргумент err заполняется объектом ошибки, содержащим информацию о проблеме: файл не найден или недостаточно полномочий.
Как правило, методы, использующие колбэк для обработки ошибок, не могут определить, насколько важна выявленная ошибка. Они возвращают ошибку пользователю для обработки. Важно контролировать поток содержимого колбэка, проверять функцию на наличие ошибки, прежде чем пытаться получить доступ к результату операции.
Чтобы использовать шаблон колбэка с первым аргументом-ошибкой в собственных асинхронных функциях, нужно принять функцию в качестве последнего аргумента и вызвать её:
function square(num, callback) < if (typeof callback !== 'function') < throw new TypeError(`Callback must be a function. Got: $`); > // simulate async operation setTimeout(() => < if (typeof num !== 'number') < // if an error occurs, it is passed as the first argument to the callback callback(new TypeError(`Expected number but got: $`)); return; > const result = num * num; // callback is invoked after the operation completes with the result callback(null, result); >, 100); >
Любой вызывающий функцию square должен пройти через колбэк, чтобы получить доступ к нужному результату или ошибке.
Не нужно непосредственно обрабатывать ошибку в функции колбэка. Её можно распространить вверх по стеку, передав на другой колбэк. Но сначала убедитесь, что вы не генерируете исключение внутри функции. Асинхронное исключение невозможно отследить, потому что окружающий блок try / catch завершается до выполнения колбэка. Следовательно, исключение будет распространяться на вершину стека, что приведёт к завершению работы приложения. Исключение — когда обработчик зарегистрирован для process.on (‘uncaughtException’) .
try < square('8', (err, result) => < if (err) < throw err; // not recommended >console.log(result); >); > catch (err) < // This won't work console.error("Caught error: ", err); >

Отклонение обещаний
Обещания в JavaScript — это актуальный способ выполнения асинхронных операций в Node.js. Они предпочтительнее колбэков из-за лучшего потока, который соответствует современным способам анализа программ, особенно с шаблоном async / await . Любой API-интерфейс Node.js, использующий колбэки с ошибкой для асинхронной обработки ошибок, может быть преобразован в обещания с помощью встроенного метода util.promisify () . Например, заставить метод fs.readFile () использовать обещания можно так:
const fs = require('fs'); const util = require('util'); const readFile = util.promisify(fs.readFile);
Переменная readFile — это версия fs.readFile () с обещаниями, в которой отклонения обещаний используются для сообщения об ошибках. Эти ошибки можно отследить, связав метод catch :
readFile('/path/to/file.txt') .then((result) => console.log(result)) .catch((err) => console.error(err));
Также можно использовать обещанные API в функциях async . Так выглядит основной способ использования обещаний в современном JavaScript: в нём код читается как синхронный, и для обработки ошибок применяют знакомый механизм try / catch . Перед асинхронным запуском важно использовать await , чтобы обещание было выполнено или отклонено до того, как функция возобновит выполнение. При отклонении обещания выражение await выбрасывает отклонённое значение, которое впоследствии попадает в окружающий блок catch .
(async function callReadFile() < try < const result = await readFile('/path/to/file.txt'); console.log(result); >catch (err) < console.error(err); >>)();
Обещанияможно использовать в асинхронных функциях, возвращая обещание из функции и помещая код функции в обратный вызов обещания. Если есть ошибка, её стоит отклонить (reject) с помощью объекта Error. В противном случае можно разрешить (resolve) обещание с результатом, чтобы оно было доступно в цепочке метода .then или напрямую как значение функции async при использовании async / await .
function square(num) < return new Promise((resolve, reject) => < setTimeout(() =>< if (typeof num !== 'number') < reject(new TypeError(`Expected number but got: $`)); > const result = num * num; resolve(result); >, 100); >); > square('8') .then((result) => console.log(result)) .catch((err) => console.error(err));
Источники событий
Другой шаблон, подходящий для работы с длительными асинхронными операциями, которые могут приводить к множественным ошибкам или результатам, — это возврат EventEmitter из функции и выдача события как для успешного, так и для неудачного случая:
const < EventEmitter >= require('events'); function emitCount() < const emitter = new EventEmitter(); let count = 0; // Async operation const interval = setInterval(() =>< count++; if (count % 4 == 0) < emitter.emit( 'error', new Error(`Something went wrong on count: $`) ); return; > emitter.emit('success', count); if (count === 10) < clearInterval(interval); emitter.emit('end'); >>, 1000); return emitter; >
Функция emitCount () возвращает новый эмиттер событий, который сообщает об успешном исходе в асинхронной операции. Она увеличивает значение переменной count и каждую секунду генерирует событие успеха и событие ошибки, если значение count делится на 4. Когда count достигает 10, генерируется событие завершения. Этот шаблон позволяет передавать результаты по мере их поступления вместо ожидания завершения всей операции.
Вот как можно отслеживать и реагировать на каждое из событий, генерируемых функцией emitCount () :
const counter = emitCount(); counter.on('success', (count) => < console.log(`Count is: $`); >); counter.on('error', (err) => < console.error(err.message); >); counter.on('end', () => < console.info('Counter has ended'); >);
Функция колбэка для каждого прослушивателя событий выполняется независимо, как только событие генерируется. Событие ошибки (error) — это особый случай для Node.js, потому что при отсутствии прослушивателя процесс Node.js выходит из строя. Вы можете закомментировать прослушиватель событий ошибки выше и запустить программу, чтобы увидеть, что произойдёт.

Расширение объекта ошибки
Необходимо создавать собственные пользовательские классы ошибок, чтобы лучше отражать разные типы ошибок: класс ValidationError для ошибок, возникающих при проверке пользовательского ввода, класс DatabaseError для операций с базами данных, TimeoutError для операций, для которых истекло назначенное им время ожидания.
Пользовательские классы ошибок, расширяющие объект Error , сохранят основные свойства ошибки: сообщение ( message ), имя ( name ) и стек ( stack ). Но у них есть собственные свойства. ValidationError можно улучшить, добавив значимые свойства — часть ввода, вызвавшую ошибку.
Вот как можно расширить встроенный объект Error в Node.js:
class ApplicationError extends Error < constructor(message) < super(message); // name is set to the name of the class this.name = this.constructor.name; >> class ValidationError extends ApplicationError < constructor(message, cause) < super(message); this.cause = cause >>
Класс ApplicationError — общая ошибка, а класс ValidationError представляет любую ошибку, возникающую при проверке ввода данных пользователем. Он наследуется от класса ApplicationError и дополняет его свойством cause для указания ввода, вызвавшего ошибку. Пользовательские классы ошибки можно использовать, как и обычные:
function validateInput(input) < if (!input) < throw new ValidationError('Only truthy inputs allowed', input); >return input; > try < validateInput(userJson); >catch (err) < if (err instanceof ValidationError) < console.error(`Validation error: $, caused by: $`); return; > console.error(`Other error: $`); >

Ключевое слово instanceof следует использовать для проверки конкретного типа ошибки. Не используйте имя ошибки для проверки типа, как в err.name === ‘ValidationError’ : это не сработает, если ошибка получена из подкласса ValidationError .
Типы ошибок
Типы ошибок можно разделить на две основные категории: ошибки программиста и операционные проблемы. К первому типу можно отнести неудачные или неправильные аргументы функции, в то время как временные сбои при работе с внешними API однозначно относятся ко второй категории.
Операционные ошибки
Операционные ошибки — это предсказуемые ошибки, которые возникают в процессе выполнения приложения. Это не обязательно баги, чаще это даже внешние обстоятельства, способные нарушить ход выполнения программы. В таких случаях можно полностью понять влияние ошибки на процессы:
- Запрос API не выполняется по какой-либо причине (например, сервер не работает или превышен лимит скорости).
- Соединение с базой данных потеряно, например, из-за неисправного сетевого соединения.
- ОС не может выполнить запрос на открытие файла или запись в него.
- Пользователь отправляет на сервер недопустимые данные: неверный номер телефона или адрес электронной почты.
Ошибки программиста
Ошибки программиста — это ошибки в логике или синтаксисе программы, которые можно исправить только путём изменения исходного кода. Ошибки этого типа невозможно обработать, потому что это недочёты в программе:
- Синтаксические ошибки: незакрытая фигурная скобка.
- Ошибки типа при попытке сделать что-то неправильное: выполнение операций с операндами несовпадающих типов.
- Неверные параметры при вызове функции.
- Ссылки на ошибки при неправильном написании имени переменной, функции или свойства.
- Попытка получить доступ к местоположению за концом массива.
- Неспособность обработать операционную ошибку.
Обработка операционных ошибок
Операционные ошибки в большинстве случаев предсказуемы. Их обработка — это рассмотрение вероятности неудачного завершения операции, возможных причин и последствий. Рассмотрим несколько стратегий обработки операционных ошибок в Node.js.
Сообщить об ошибке в стек
Во многих случаях лучше остановить выполнение программы, очистить все незавершённые процессы и сообщить об ошибке в стек. Зачастую это единственный способ исправить ошибку, когда функция, в которой она возникла, находится дальше по стеку.
Повторить операцию
Сетевые запросы к внешним службам иногда могут завершаться ошибкой, даже если запрос полностью верен. Это случается из-за сбоя и неполадках сети или перегрузке сервера. Можно повторить запрос несколько раз, пока он не будет успешно завершён или пока не будет достигнуто максимальное количество повторных попыток. Первое, что нужно сделать, — это определить, уместно ли повторить запрос. Если исходный код состояния HTTP ответа — 500, 503 или 429, повторте запрос через некоторое время.
Проверьте, присутствует ли в ответе HTTP-заголовок Retry-After. Он указывает на точное время ожидания перед выполнением последующего запроса. Если его нет, необходимо отложить последующий запрос и постепенно увеличивать временной промежуток для каждой повторной попытки. Этот метод известен как стратегия экспоненциального отката. Нужно ещё определить максимальное время задержки и число запросов до отказа от дальнейших попыток.
Отправить ошибку клиенту
По умолчанию пользователи вводят данные неправильно. Поэтому первое, что нужно сделать перед запуском каких-либо процессов, — проверить введённые данные и незамедлительно сообщить пользователю о любых ошибках. При обработке ошибок клиента обязательно включите всю информацию, необходимую для создания сообщения об ошибке и имеющую смысл для пользователя.
Прервать программу.
В случае неисправимых системных ошибок разумный выход — зарегистрировать ошибку и немедленно завершить работу программы. Если исключение невозможно исправить на уровне JavaScript, то, возможно, не получится корректно завершить работу сервера. Тогда нужен системный администратор, способный всё исправить.
Предотвращение ошибок программиста
Ошибки программиста сами по себе не могут быть обработаны, потому что их причина в коде или в логике. Однако ошибаться можно реже.
Принять TypeScript
TypeScript — это строго типизированное надмножество JavaScript. Основная цель его проектирования — статическая идентификация потенциально ошибочных конструкций без штрафных санкций во время выполнения. Принимая TypeScript в проекте (с максимально возможными параметрами компилятора), можно устранить целый класс ошибок программиста в ходе компиляции.
Когда проект на TypeScript, такие ошибки, как undefined is not a function , синтаксические или ссылочные ошибки, исчезают из кодовой базы. Перенос на TypeScript можно выполнять постепенно. Для быстрой миграции есть инструмент ts-migrate.
Определить поведение для неверных параметров
Многие ошибки возникают из-за передачи неверных параметров. Это может быть связано не только с очевидными ошибками, такими как передача строки вместо числа, но и с небольшими погрешностями, когда аргумент функции имеет правильный тип, но выходит за пределы диапазона, который функция способна обработать. Когда функция вызывается таким образом, она может выдать неверное значение, например NaN . Когда сбой обнаруживается, сперва трудно определить его причину.
При работе с неверными параметрами и определяйте их поведение, либо выдавая ошибку, либо возвращая специальное значение, такое как null , undefined или -1 , когда проблема может быть решена локально. Первый вариант— это подход, используемый JSON.parse () , который выдаёт исключение SyntaxError , если строка для синтаксического анализа недействительна. Второй вариант — метод string.indexOf () .
Автоматизированное тестирование
Автоматизированные наборы тестов повышает вероятность исправления ошибок. Тесты помогают выяснить, как функция работает с нетипичными значениями. Для модульного тестирования подходят среды, такие как Jest или Mocha.
Неперехваченные исключения и необработанные отклонения обещаний
Неперехваченные исключения и необработанные отклонения обещаний вызываются ошибками программиста. Событие uncaughtException генерируется, когда исключение не перехватывается до того как достигнет цикла обработки событий. При обнаружении неперехваченного исключения приложение немедленно выходит из строя. Для переопределения такого поведения всегда можно добавить обработчик события:
// unsafe process.on('uncaughtException', (err) => < console.error(err); >);
Но неперехваченное исключение указывает на то, что приложение находится в неопределённом состоянии. Поэтому попытка возобновить работу в обычном режиме без восстановления после ошибки небезопасна и может привести к утечке памяти и зависанию сокетов. Лучше использовать обработчик uncaught Exception для очистки всех выделенных ресурсов, закрытия соединений и ведения лога ошибок для оценки перед завершением процесса.
// better process.on('uncaughtException', (err) => < Honeybadger.notify(error); // log the error in a permanent storage // attempt a gracefully shutdown server.close(() =>< process.exit(1); // then exit >); // If a graceful shutdown is not achieved after 1 second, // shut down the process completely setTimeout(() => < process.abort(); // exit immediately and generate a core dump file >, 1000).unref() >);
Событие unhandledRejection генерируется, когда отклонённое обещание не обрабатывается блоком catch . В отличие от uncaughtException , эти события не вызывают немедленного сбоя приложения. Однако необработанные отклонения обещаний сейчас признаны устаревшими и могут немедленно завершить процесс в следующих релизах Node.js. Отслеживать необработанные отклонения обещаний можно с помощью прослушивателя событий unhandledRejection :
process.on('unhandledRejection', (reason, promise) => < Honeybadger.notify(< message: 'Unhandled promise rejection', params: < promise, reason, >, >); server.close(() => < process.exit(1); >); setTimeout(() => < process.abort(); >, 1000).unref() >);
Серверы необходимо запускать с помощью диспетчера процессов, который автоматически перезапустит их в случае сбоя. Распространённый вариант — PM2, но для Linux существуют также systemd и upstart , а пользователи Docker могут использовать собственную политику перезапуска. По завершении всех процессов стабильное обслуживание будет восстановлено почти мгновенно, а у вас будт информация о неперехваченном исключении. Можно запутсить несколько процессов и применить балансировщик нагрузки для распределения входящих запросов. Это поможет предотвратить простои.
Централизованная отчётность об ошибках
Ни одна стратегия обработки ошибок не будет полной без надёжной стратегии ведения журнала ошибок. Когда происходит сбой, важно узаписать как можно больше информации о проблеме. Централизация логов позволяет оценить, что происходит в коде.
Honeybadger предоставляет всё необходимое для отслеживания ошибок. Интегрируется так:
Установите пакет
Используйте npm для установки пакета:
$ npm install @honeybadger-io/js —save
Импортируйте библиотеку
Импортируйте библиотеку и настройте её с помощью ключа API, чтобы получать сообщения об ошибках:
const Honeybadger = require('@honeybadger-io/js'); Honeybadger.configure(< apiKey: '[ YOUR API KEY HERE ]' >); Сообщите об ошибках Метоодом notify (): try < // . error producing code >catch(error)
Просмотрите полную документацию или ознакомьтесь с образцом Node.js / Express на GitHub.
Без обработки ошибок не бывает надёжного софта.
Спасибо за внимание и удачного кода!
