Hello World. Простейший Node JS Hacker News сервер
кодинг2017 / 08 / 31

Hello World. Простейший Node JS Hacker News сервер

Ave $USER!

Node.JS - это платформа для выполнения JavaScript основаная на движке V8. В этой статье мы установим node.js и напишем простой веб-сервис, который будет показывать топ 10 новостей с Hacker News.

Установка

Windows

Для установки нужно перейти на сайт nodejs.org скачать и установить.

Примечание: Если вы используете Visual Studio 2015, установите переменную окружения GYP_MSVS_VERSION=2015, что бы собирать модули Node JS с помощью Visual Studio 2015

Ubuntu

curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
sudo apt-get install -y nodejs

MacOS

brew install node

Проверка

Вместе с node.js будет установлен менеджер пакетов npm. Что бы проверить версию node и npm введем команды

node -v
npm -v

На момент написания статьи я использую node версии 8.4.0. Давайте запустим простейший http сервер на node.js.

Создание проекта

У меня все проекты находятся по пути ~/projects/JavaScript. Откроем консоль, создадим директорию для нашего проекта hacker-blog и перейдем в нее. Сначала нам нужно инициализировать проект командой npm init -y. В результате должен появиться файл package.json. Откроем его и изменим секцию scripts:

...
  "scripts": {
    "start": "nodemon index.js"
  }
...

Далее установим необходимые модули с помощью следующей команды:

npm install --save express isomorphic-fetch nodemon

Немного о модулях:

  • express - мини веб-фреймворк, с очень удобным API
  • isomorphic-fetch - модуль позволяет использовать Fetch API на стороне сервера
  • nodemon - модуль позволяет автоматически перезапускать скрипт при его изменении

Сначала создадим шаблон для нашего сайта в обычном index.html файле

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Top 10 Hacker News stories</title>
  <style>
    html, body {
      margin: 0;
      min-height: 100%;
      background-color: #f6f6ef;
    }

    .title {
      background-color: #f60;
      display: flex;
      justify-content: center;
    }
    .stories {
      display: flex;
      flex-direction: column;
    }

    .story {
      text-decoration: none;
      color: #333;
    }
    .story__details {
      font-size: 14px;
      color: #888
    }
  </style>
</head>
<body>
<h1 class="title">Top 10 Hacker News</h1>
<ol class="stories">
  <!--STORIES-->
</ol>
</body>
</html>

Здесь все просто, в списке есть комментарий <!--STORIES-->, который будем использовать как метку, куда поместить наши новости. Стили можете изменить на свой вкус и цвет.

Теперь создадим файл service.js где будут функции вызова API Hacker News:

require('isomorphic-fetch');
// Т.к. у нас установлен node 8+ то можем использовать async/await, 
// что делает код более читабельным и линейным, нежели лапша из `Promise.then`.

async function getTopStoriesIds(count) {
// Здесь используется URL `https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty`,
// который возвращает массив из числовых значений топ новостей, где мы получам `count` первых значений.

  const response = await fetch('https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty');
  const ids = await response.json();
  return ids.slice(0, count);
}

async function fetchTopStories(ids) {
// Здесь мы получем информацию о каждой новости по ее номеру 
// через URL `https://hacker-news.firebaseio.com/v0/item/${id}.json`.

  return Promise.all(ids.map(async id => {
    const response = await fetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json`);
    return response.json();
  }));
}

module.exports = {
  getTopStoriesIds,
  fetchTopStories,
};

И наконец создадим файл index.js, это и будет наш сервер:

// Импортируем установленные модули
const fs = require('fs');
const Express = require('express');
const app = Express();

// Импортируем наш сервис
const { getTopStoriesIds, fetchTopStories } = require('./services');

// Записываем шаблон в строку
const template = fs.readFileSync('./index.html', 'utf8');

// Функция для отрисовки новости
function renderStory({ url, title, by, time }) {
  return `
<li class="story">
  <a href="${url}" class="story" target="_blank">${title}</a>
  <div class="story__details">by ${by} | ${new Date(time * 1000).toLocaleString()}</div>
</li>
`;
}

// Задаем обрабатчик HTTP запроса
app.get('/', async (req, res) => {
  try {
    // Ожидаем получение топ 10 новостей
    const topIds = await getTopStoriesIds(10);
    // Ожидаем получение информации о новостях
    const items = await fetchTopStories(topIds);
    // Отрисовываем их в строки
    const stories = items.map(renderStory);

    // Задаем время актуальности запроса
    res.setHeader('Cache-Control', 'public, max-age=100');
    res.setHeader('Expires', new Date(Date.now() + 100).toUTCString());
    
    // Заменяем нашу метку на строки и отправляем ответ
    res.end(template.replace('<!--STORIES-->', stories.join('\n')));
    
  } catch (error) {
    // Обрабатываем ошибку, если что-то не так
    console.error(error.stack);
    res.status(500).send('Something broke!');
  }
});

const port = process.env.NODE_PORT || 9191;

// Запускаем сервер
app.listen(port, () => {
  console.log(`Served from http://localhost:${port}`);
});  

Запуск

Теперь запустим наш сервер командой:

npm start

Перейдя по адресу 127.0.0.1:9191 мы увидим нашу страницу:

Исходники