Строим свой full-stack на JavaScript
В мире JavaScript очень легко набрать свой стек технологий, используя набор небольших пакетов, каждый из которых решают свою конкретную проблему. И это хорошо, c одной стороны, а с другой стороны, у вас особо нет выбора — фреймворки которые выполняют широкий спектр задач в JavaScript не популярны.
В этом цикле статей я хочу поделиться своим практическим опытом построения JS стека.
Я перешел на full-stack JavaScript из .NET. И уже после того как я освоил основы Node, мне пришлось еще уйму времени потратить на построение своего базового стека технологий и способа организации кода. К сожалению, существует не так много примеров конечных приложений на JS, где видно как решать основные, для большинства проектов задачи, такие как: обработка ошибок, доступ к БД, организация кода, авторизация и прочее.
Для того, чтобы всегда иметь под рукой пример использования базовых технологий, был написан Contoso Express, пример full-stack приложения на JS. В нашей команде, используем этот проект, как стартовый шаблон для других проектов, поэтому он будет поддерживаться и обновляться в дальнейшем.
Contoso Express это ремейк известного Microsoft учебника, для обучения основам разработки .NET веб проектов.
Оригинальный Contoso Univerisity
Для многих частей стека, сложно остановиться на одной альтернативе. Поэтому для некоторых технологий есть альтернативные имплементации. Смотри ветки ALT в Contoso Express repository.
Эти статьи для тех, кто уже знает основы, если вам нужно восполнить знания в какой-то области, поищите нужное в списке дополнительных ресурсов.
Приступим!
Почему JavaScript
Причин может быть много, вот мой топ лист:
Простота: В основном, библиотеки в Node имеют простые API с которыми легко разобраться и которые работают интуитивно понятным образом. Если не получилось с одной библиотекой, как правило, несложно найти хорошую альтернативу.
Контроль: Программист сам строит инфраструктуру проекта, выбирая и объединяя небольшие модули для конкретных задач. Это требует больше времени, но результат стоит того. Разобравшись один раз, полученный опыт легко применять в дальнейшем.
Универсальность: JavaScript изначально работал только на клиенте. Вначале вместе с Node он перебрался на сервер, а совсем недавно на нем стало можно успешно писать десктопные (Eletctron) и мобильные приложения. Причем, для мобильных приложений есть опция гибридных приложений (используется обертка над браузером (Cordova)) или приложений с нативным интерфейсом (ReactNative, NativeScript). Для Node существует огромное множество библиотек и его легко интегрировать с другими технологиями, базы данных, облачные технологии, различные форматы и протоколы, найдется все.
Легкое развертывание: Node очень легко разворачивать на сервере: как на Linux, так и на Windows. После многих лет работы с .NET, деплоймент каждый раз был для меня неприятным испытанием, на Node этот процесс даже приносит удовольствие. Это просто надо попробовать.
Производительность: Node асинхронен и не блокирует процесс выполнения во время длительных операций, таких как вычитка файла или обращение к базе данных. Это позволяет достичь высокого уровня производительности при использовании единственного потока (single threaded environment). C другой стороны, вычисления в JavaScript медленнее чем в статически типизированных языках. Для большинства проектов это не проблема. Если нужны вычисления, а не просто преобразования данных, то лучше написать отдельный сервис на чем-то другом.
Один язык на сервере и клиенте: Это удобно, так как позволяет, без усилий переносить код между клиентом и сервером, легче в разработке и поддержке.
- Язык JavaScript: JavaScript очень гибок и прост в использовании, многие недостатки языка исправлены в последней версии ES6, типизацию опционально можно добавить, используя TypeScript.
И это далеко не полный список.
Варианты языка JS
Современный JavaScript можно писать в нескольких вариантах:
- ES5
- ES6 / ES7 next
- Языки компилируемые в JS (TypeScript и другие)
Стандарты JavaScript
ES5 — версия JavaScript от 2009 года, полностью поддерживаемая всеми современными браузерами и Node.
ES6 — недавнее утвержденное обновление языка. Разработка поддержки стандарта всех JS движках пока еще в процессе разработки.
- ES7 / Next — еще не утвержденные новые фичи JS.
Взглянуть на текущее состояние поддержки ES6 можно на
Kangax ES6.
Таким образом, в ближайшее время разрабатывать клиентскую часть сразу на ES6 нельзя, потому что поддержки во всех браузерах еще нет.
Для Node используется V8 движок, текущая стабильная LTS версия которого (4.x) не поддерживает все новые особенности ES6.
LTS (long term support) это версия NodeJs, рекомендуемая для использования на продакшeне. Следующая LTS Node ожидается в октябре 2016 и в ней уже есть поддержка большинства возможностей ES6.
Транспайлинг (transpiling)
Для того чтоб бы использовать фичи ES6/ES7 существует несколько транспайлеров которые преобразуют код написанный на ES6 в ES5.
Обратите внимание на разницу между транспиляцией и компиляцией: тут.
Babel самый популярный транспайлер из ES6/Next в ES5.
TypeScript
TypeScript это язык расширение JavaScript, которое добавляет возможность статической типизации. Типы в TypeScript используются для проверки корректности кода и дополнительных возможностей рефакторинга и авто-подсказок. Когда TypeScript транспилируется в JS, все определения типов опускаются.
TypeScript поддерживает многие ES6/ESNext возможности и может использоваться как Транспайлер (вместо Babel).
Кроме того в TypeScript существуют дополнительные конструкции, которых нет в JS — энумы, наследование и полиморфизм в классах и прочее. Они транспилируются в JS с помощью вспомогательного JS кода.
Не все возможности TypeScript одинаково полезны перечислю основные категории:
Транспиляция ES6/ESNext — TS отлично с этим справляется, хотя и уступает Babel в некоторых моментах, например async/await в TypeScript пока только доступно если транпилировать в ES6 версию. Больше об этом в следующей статье.
Статическая типизация в коде приложения — это основная причина использовать TS, типы в TS опциональны, если тип не указан, предполагается тип "any", что значит в этот тип можно записать и прочитать из него любые поля, это делает перевод существующего кода JS на TS, гораздо легче, не нужно добавлять типы везде, а только там где это имеет смысл. При этом даже если вы не описываете типы в TS, у вас появляется много дополнительных проверок, которые помогают обнаруживать тривиальные ошибки-опечатки, еще до того, как вы запускаете приложение. Дополнительно у вас хорошо работают подсказки в коде и не нужно лишний раз смотреть какие методы есть у модуля и какие у них сигнатуры.
Типизация сторонних библиотек — TS позволяет описывать структуру сторонних библиотек (например lodash или express), это позволяет контролировать, что вы вызываете методы с правильными параметрами и позволяет видеть методы и их сигнатуры без использования документации. К сожалению качество таких описаний часто оставляет желать лучшего и когда в описании нет нужной сигнатуры ее нужно добавлять вручную. Иногда легче не использовать сторонние описания (работать с библиотекой как с типом "any"). Проблема в том, что сейчас описания библиотек в TS и сами библиотеки чаще всего пишут разные люди. Скорее всего ситуация будет улучшаться с ростом популярности TS.
- Возможности TS, которых нет в JS — это я бы не рекомендовал к использованию без необходимости или если вы не используете фреймворки написанные непосредственно на TS (например Angular2). Использование таких возможностей делает конвертацию TS<->JS гораздо сложнее, они чаще изменяются между разными версиями TS.
Что выбрать
Я бы рекомендовал выбирать между ES6 и TypeScript. ES6 имеет очень много полезных дополнений, которые делают разработку легче и приятнее и это стоит того, чтобы потратить больше времени на первоначальную настройку. На своих проектах я перешел на TypeScript, потому что это действительно серьезно улучшает процесс разработки, хотя и требует гораздо больших усилий по настройке и интеграции. Что бы вы не выбрали, хорошо если вы делаете осознанный выбор, поработав и с тем и с другим.
Среда разработки
WebStorm — умная среда разработки со множеством встроенных функций. Хорошая поддержка для TypeScript. Платная. Подробности настройки для contoso express на wiki-странице проекта. WebStorm основная IDE в нашей команде.
Visual Studio Code — бесплатная среда от Microsoft. Наилучшая поддержка для TypeScript, появилась недавно, нет некоторых привычных функций. Быстро развивается, полностью сделана и поддерживает плагины на JavaScript.
Другие — можно пользоваться и другими JavaScript IDE, такими как Atom, Sublime, Brackets. TypeScript в той или иной степени поддерживается везде.
Подбираем npm пакеты
Работать с npm очень просто. Просто устанавливать и просто публиковать свои пакеты. Вследствии этого пакетов на npm великое множество (на текущий момент 330.000+).
Подобрать правильный пакет достаточно сложно. По любому запросу на npmjs.com найдется множество пакетов, при этом не факт, что там будут предоставлены основные пакеты для решения нужной задачи.
Выбирая пакет стоит обратить внимание на: количество закачек (популярность) и как часто пакет обновляется (коммиты в гитхаб репозитории). Найдя нужный пакет, можно поискать информацию в интернете, так можно найти другие пакеты выполняющие схожие задачи.
Есть сайт с альтернативным поиском по npm npms.io, там сразу просчитываются такие характеристики, как популярность и регулярность поддержки.
В дальнейших статьях я буду описывать основные опции по пакетам для базовых задач разработки. Если имя пакета в кавычках (например "bluebird"), это значит это имя с которым пакет зарегистрирован в npm и его можно посмотреть по ссылке https://www.npmjs.com/package/{package_name} (т.е. в данном случае https://www.npmjs.com/package/bluebird).
Базы данных
SQL или NoSQL?
На этот вопрос нельзя ответить однозначно, зависит от конкретной ситуации. Самыми популярными опциями для SQL/NoSql являются PostgreSQL/MongoDb. Недавнее добавление JSON полей в PostgreSQL, делает его очень привлекательным вариантом соединяющим в себе лучшее из миров SQL/NoSql. Но, несмотря на это, MongoDb по-прежнему самая популярная база данных для Node, и может быть легче для работы, особенно для тех, у кого не было предыдущего опыта работы с SQL базами данных.
Доступ к базе данных
Работая с базой данных вы можете использовать доступ напрямую с помощью драйвера базы данных или каккую-то ORM абстракцию более высокого уровня. Если у вас не много взаимодействий с базой данных, то лучше использовать доступ напрямую или абстракцию низкого уровня, такую как Knex (для SQL баз данных).
ORM
Sequelize — самая популярная ORM для SQL баз данных. Она предоставляет высокий уровень абстракции над БД схемой и поддерживает основные SQL диалекты (PostgreSQL, MySQL, SQLite and MSSQL). Используется в Contoso Express.
Knex — это абстракция более низкого уровня. Больше как конструктор запросов, чем полоценная ORM. Поддерживает большее количество диалектов и дает больше контроля над генерируемым SQL. Имеются функции построения схемы БД и ее миграций.
Bookshelf — eще одна популярная ORM основанная на Knex, уровень абстракции ниже чем в Sequelize и многие вещи нужно определять вручную.
Mongoose — Самая популярная ORM для самой популярной в JS базы данных MongoDB
Прямое подключение
Для всех основных баз данных существуют драйверы хорошего качества, для прямого соединения. Для Postgres используйте "pg" или "pg-promise" пакеты.
Что дальше
В следующей статье я расскажу про работу с JS на сервере. Она будет более практичной и покроет такие вещи, как выбор веб фреймворка, организация структуры проекта, работа с конфигами, авторизация, логирование, обработка ошибок и другое.
Буду рад замечаниям комментариям, особенно, непосредственно, по коду проекта Contoso Express :)
Спасибо всем кто дочитал до конца! Stay tuned!
Комментарии (23)
igrishaev
09.09.2016 10:06используя набор небольших пакетов, каждый из которых решают свою конкретную проблему
вроде этого?
savostin
09.09.2016 10:27var toString = {}.toString; module.exports = Array.isArray || function (arr) { return toString.call(arr) == '[object Array]'; };
А-А-А-А-А!!!1
Там даже тест есть, жесть.igrishaev
09.09.2016 11:07-4У него 26 миллионов загрузок в месяц, если что. Стек на Js — это как строить дом из пивных банок.
AdeptMech
14.09.2016 00:08Стек на JS абсолютно жизнеспособен. А вот всякий трешак инклюдить себе в проект действительно не нужно. И совершенно неважно сколько миллионов идиотов это делают.
indieloki
10.09.2016 03:39-1А что плохого в тесте? Оно написано «один раз и на всю жизнь». Например — выходит новая фича языка, автор меняет реализацию на дающую больший перфоманс и проверяет что все работает.
Yeggor
10.09.2016 03:36Это уже перегиб, для подобных задач я использую lodash, а если пакет это всего одна функция, то лучше добавить скопипастить эту функцию как утилитный метод проекта. Я имел в виду небольшие пакеты в плане: пакет логирования, пакет отправки имейлов, т.д.
sentyaev
09.09.2016 12:21-2Выбирая пакет стоит обратить внимание на: количество закачек (популярность) и как часто пакет обновляется (коммиты в гитхаб репозитории).
Всегда удивлялся таким аргументам.sentyaev
09.09.2016 16:07Специально для тех кто в танке поясню.
Я выбираю библиотеки не по количеству звездочек и количеству коммитов, а проверяю есть ли в библиотеке функции которые мне нужны и корректность их работы. КО.amakhrov
12.09.2016 12:53Корректность работы можно проверить, только установив пакет и прикрутив его к своему приложению. То есть это не поможет с начальным выбором.
- Зачастую поиск выдаст несколько пакетов, в которых, судя по документации, есть все те (или примерно те) функции, которые нужны. И вот тут-то из них выбираем по количеству скачиваний и частоте коммитов / дате последнего обновления (банально проверяем, что пакет еще поддерживается в принципе).
sentyaev
12.09.2016 13:55Корректность работы можно проверить, только установив пакет и прикрутив его к своему приложению. То есть это не поможет с начальным выбором.
Прикручивать не нужно, достаточно написать тесты.
Зачастую поиск выдаст несколько пакетов, в которых, судя по документации, есть все те (или примерно те) функции, которые нужны. И вот тут-то из них выбираем по количеству скачиваний и частоте коммитов / дате последнего обновления (банально проверяем, что пакет еще поддерживается в принципе).
Вы затронули правильную тему.
Давайте разделим поиск библиотек/фрэймворков хотябы на две части.
1. Поиск стандартных библиотек. Тут как раз проблем нет, куча документации, блогов и т.д. Но если используете что-то впервые, все равно нужно хотябы что-то посложнее ToDo List написать, чтобы понять подходит оно вам или нет.
2. Поиск специфичных библиотек. Не факт что такие библиотеки будут иметь много звездочек (и обычно там их очень мало). По опыту могу сказать, что в таких случаях приходится код читать, чтобы понять оно или нет.
Есть еще один момент, если брать только популярные библиотеки, то получается вы себя искуственно ограничиваете, и находитесь в роли догоняющего. Я не фанат подхода — использовать только все самое новое, но бывают случаи, достаточно редко, но бывают.
Sergey2020
10.09.2016 03:38-1читаю все — умиляюсь, только когда нескольким компаниям закинул запрос на создание игры, подобной slither.io — все потерялись, а бразильский 19-летний мальчик за три дня написал) а там то всего Requirements
NodeJS
NPM
Bower
Socket.IO
Express
rock
Это ES7-то не утвержден? :-)
FasSsko
так-то нет ES7, есть ES2015 aka ES6 и ES2016 — в который вошли 2 фичи(includes и **)
rock
Как это нет? Печаль-беда, а я столько времени посветил тому, чтоб все и ES7 могли пользоваться, видать всё зря — вы всё испортили… Вот только чуть менее, чем всем без разницы — как ES2015 называли ES6 — так и ES2016 называют ES7 — и ES2017 будут называть ES8.