как запустить сервер node js
Для запуска сервера Node.js нужно выполнить несколько шагов:
- Установить Node.js на компьютер, если он еще не установлен. Скачать установщик можно на официальном сайте Node.js.
- Создать файл с расширением .js , в котором будет содержаться код для запуска сервера. Например, создадим файл server.js .
- В файле server.js нужно написать код для создания сервера. Вот простой пример:
const http = require('http'); const hostname = '127.0.0.1'; const port = 3000; const server = http.createServer((req, res) => res.statusCode = 200; res.setHeader('Content-Type', 'text/plain'); res.end('Hello, World!\n'); >); server.listen(port, hostname, () => console.log(`Server running at http://$hostname>:$port>/`); >);
Этот код создает сервер на локальном хосте (127.0.0.1) и порту 3000. Когда сервер запущен, он будет отвечать на любые запросы с сообщением «Hello, World!».
- Запустить сервер нужно в командной строке (терминале) из папки, где находится файл server.js . Например, если файл находится в папке myproject , то нужно перейти в эту папку командой cd myproject , а затем запустить сервер командой node server.js .
- Если все настроено правильно, то в консоли должно появиться сообщение «Server running at http://127.0.0.1:3000/ «, а в браузере можно открыть страницу по адресу http://127.0.0.1:3000/ и увидеть сообщение «Hello, World!».
Свой веб-сервер на NodeJS, и ни единого фреймворка. Часть 1
Для многих людей JavaScript ассоциативно связан с обилием разнообразных фреймворков и библиотек. Разумеется, инструменты, которые помогают нам каждый день — это хорошо, но, мне кажется, нужно искать некий баланс между использованием инструментов и прокрастинацией, а также знать, как работают вещи, которыми ты пользуешься. Поэтому, когда я только сел разбираться с NodeJS, мне было особенно интересно написать полноценный веб-сервер, которым я мог бы пользоваться сам.
Новичку в NodeJS действительно может быть нелегко. JS — один из языков, в котором часто не существует единственного правильного решения конкретной задачи, а добавленные в ноду модули для работы с файловой системой, http сервером и прочими вещами, характерными для работы на сервере, затрудняют переход даже тем, кто пишет хороший код для браузеров. Тем не менее, я надеюсь, что вы знаете основы этого языка и его работы в серверном окружении, если нет, советую посмотреть замечательный скринкаст, который поможет разобраться в основах. И последнее — я не претендую на какой-то исключительно правильный код и буду рад услышать критику — мы все учимся, и это отличный способ получать знания.
Начнём с файловой структуры
Исходная папка nodejs хранится на сервере по пути /var/www/html/. В ней и будет наш веб-сервер. Дальше всё просто: создаём в ней директорию routing, в которой будет лежать наш скрипт index.js, а также 4 папки — dynamic, static, nopage и main — для динамически генерируемых страниц, статики, страницы 404 и главной страницы. Выглядит всё это так:
nodejs
—routing
—-dynamic
—-nopage
—-static
—-main
—-index.js
Создаём наш сервер
Отлично, с файловой структурой более-менее определились. Теперь создаём в исходной папке файл server.js со следующим содержимым:
// server.js // Для начала установим зависимости. const http = require('http'); const routing = require('./routing'); let server = new http.Server(function(req, res) < // API сервера будет принимать только POST-запросы и только JSON, так что записываем // всю нашу полученную информацию в переменную jsonString var jsonString = ''; res.setHeader('Content-Type', 'application/json'); req.on('data', (data) =>< // Пришла информация - записали. jsonString += data; >); req.on('end', () =>/ Информации больше нет - передаём её дальше. routing.define(req, res, jsonString); // Функцию define мы ещё не создали. > ); >); server.listen(8000, 'localhost');
Здорово! Теперь наш сервер будет принимать запросы, записывать JSON-данные, если они есть, но пока что будет вылетать с ошибкой, потому что у нас нет функции define в /routing/index.js. Время это исправить.
// /routing/index.js const define = function(req, res, postData) < res.end('Hello, Habrahabr!'); >exports.define = define;
Запускаем наш сервер:
node server.js
Заходим туда, где он слушает запросы. Если вы не меняли код, это будет localhost:8000. Ура. Ответ есть.
Замечательно. Только это не совсем то, что нам нужно от сервера, правда?
Ловим запросы к нашим API
Да, мы получили ответ, но пока что не слишком близки к конечной цели. Самое время писать логику для нашего роутера.
// /routing/index.js // Для начала установим зависимости. const url = require('url'); const fs = require('fs'); const define = function(req, res, postData) < // Теперь получаем наш адрес. Если мы переходим на localhost:3000/test, то path будет '/test' const urlParsed = url.parse(req.url, true); let path = urlParsed.pathname; // Теперь записываем полный путь к server.js. Мне это особенно нужно, так как сервер будет // висеть в systemd, и путь, о котором он будет думать, будет /etc/systemd/system/. prePath = __dirname; try < // Здесь мы пытаемся подключить модуль по ссылке. Если мы переходим на // localhost:8000/api, то скрипт идёт по пути /routing/dynamic/api, и, если находит там // index.js, берет его. Я знаю, что использовать тут try/catch не слишком правильно, и потом // переделаю через fs.readFile, но пока у вас не загруженный проект, разницу в скорости // вы не заметите. let dynPath = './dynamic/' + path; let routeDestination = require(dynPath); res.end('We have API!'); >catch (err) < // Не нашлось api? Грустно. res.end("We don't have API!"); >>; exports.define = define;
Готово. Теперь мы можем создать /routing/dynamic/api , и протестировать то, что у нас есть. Я воспользуюсь для этих целей своим готовым скриптом по адресу /dm/shortenUrl.
Определяем, есть ли страница
Мы научились находить скрипты, теперь нужно научиться находить статику. Первым делом пойдём в /routing/nopage и создадим там index.html . Просто создайте костяк html-страницы, и сделайте один-единственный заголовок h1 с текстом: «404». После этого возвращаемся в /routing/index.js , но теперь мы сосредоточимся на уже написанном блоке catch:
// /routing/index.js: блок catch catch (err) < // Находим наш путь к статическому файлу и пытаемся его прочитать. // Если вы не знаете, что это за '=>', тогда прочитайте про стрелочные функции в es6, // очень крутая штука. let filePath = prePath+'/static'+path+'/index.html'; fs.readFile(filePath, 'utf-8', (err, html) => < // Если не находим файл, пытаемся загрузить нашу страницу 404 и отдать её. // Если находим — отдаём, народ ликует и устраивает пир во имя царя-батюшки. if(err) < let nopath = '/var/www/html/nodejs/routing/nopage/index.html'; fs.readFile(nopath, (err , html) =>< if(!err) < res.writeHead(404, ); res.end(html); > // На всякий случай напишем что-то в этом духе, мало ли, иногда можно случайно // удалить что-нибудь и не заметить, но пользователи обязательно заметят. else< let text = "Something went wrong. Please contact webmaster@forgetable.ru"; res.writeHead(404, ); res.end(text); > >); > else< // Нашли файл, отдали, страница загружается. res.writeHead(200, ); res.end(html); > >); >
Воодушевляет. Теперь мы можем отдавать страницу 404, а так же html-страницы, которые мы добавляем сами в /routing/static . В моём случае страница 404 выглядит так:

Пара слов об API
Способ организации скриптов — личное дело каждого. На данный момент код в блоке try у меня такой:
let dynPath = './dynamic/' + path; let routeDestination = require(dynPath); routeDestination.promise(res,postData,req).then( result => < res.writeHead(200); res.end(result); return; >, error => < let endMessage = <>; endMessage.error = 1; endMessage.errorName = error; res.end(JSON.stringify(endMessage)); return; > );
По сути, все мои скрипты представляют собой функцию, которая передаёт замыканиями параметры и возвращает промис, и дальнейшая логика на промисах и завязана. В данном контексте такой подход кажется мне очень удобным, так отлов ошибок становится очень лёгким делом. Уже можно, в принципе, переписать эту логику на async / await, но особого смысла для себя я в этом не вижу.
Обрабатываем запросы браузера
Теперь мы уже можем пользоваться нашим сервером, и он будет возвращать страницы. Однако, если вы поместите в /routing/static/somepage ту же страницу, которая прекрасно работает, например, на апаче, вы столкнётесь с некоторыми проблемами.
Во-первых, для этого веб-сервера, как и для, наверное, всех в таком роде, нужно иначе задавать ссылки на css/js/img/… файлы. Если вам хочется подключить к странице 404 css-файл и сделать её красивой, то в случае с апачем мы создали бы в той же папке nopage файл style.css и подключили бы его, указав в тэге link следующее: ‘href=«style.css»’. Однако, теперь нам нужно писать путь иначе, а именно: «/routing/nopage/style.css».
Во-вторых, даже если мы подключим всё правильно, то ничего не произойдёт, и у нас всё ещё будет голая страница html. И вот тут мы подходим к самой последней части сегодняшней статьи — дополним скрипт, чтобы он ловил и обрабатывал запросы, которые браузер отправляет сам, читая разметку html. Ну и про favicon не забудем — возьмите фавиконку и положите её в /routing директорию нашего сервера.
Итак, переходим опять в /routing/index.js . Теперь мы будем писать код прямо перед try/catch:
// До этого мы уже получили path и prePath. Теперь осталось понять, какие запросы // мы получаем. Отсеиваем все запросы по точке, так чтобы туда попали только запросы к // файлам, например: style.css, test.js, song.mp3 if(/\./.test(path)) < if(path == 'favicon.ico') < // Если нужна фавиконка - возвращаем её, путь для неё всегда будет 'favicon.ico' // Получается, если добавить в начале prePath, будет: '/var/www/html/nodejs/routing/favicon.ico'. // Не забываем про return, чтобы сервер даже не пытался искать файлы дальше. let readStream = fs.createReadStream(prePath+path); readStream.pipe(res); return; >else< // А вот если у нас не иконка, то нам нужно понять, что это за файл, и сделать нужную // запись в res.head, чтобы браузер понял, что он получил именно то, что и ожидал. // На данный момент мне нужны css, js и mp3 от сервера, так что я заполнил только // эти случаи, но, на самом деле, стоит написать отдельный модуль для этого. if(/\.mp3$/gi.test(path)) < res.writeHead(200, < 'Content-Type': 'audio/mpeg' >); > else if(/\.css$/gi.test(path)) < res.writeHead(200, < 'Content-Type': 'text/css' >); > else if(/\.js$/gi.test(path)) < res.writeHead(200, < 'Content-Type': 'application/javascript' >); > // Опять же-таки, отдаём потом серверу и пишем return, чтобы он не шёл дальше. let readStream = fs.createReadStream(prePath+path); readStream.pipe(res); return; > >
Фух. Всё готово. Теперь можно подключить наш css-файл и увидеть нашу страницу 404 со всеми стилями:

Выводы
Ура! Мы сделали свой веб-сервер, который работает, и работает хорошо. Разумеется, это только начало работы над приложением, но самое главное уже готово — на таком веб-сервере можно поднимать любые страницы, он справляется и со статикой, и с динамическим контентом, и роутинг, на мой взгляд, выглядит удобно — достаточно просто положить соответствующий файл в static или dynamic, и он тут же подхватится, и не надо писать роутинг для каждого конкретного случая.
В общем и целом, работой сервера я очень даже доволен, и сейчас отказался от апача в сторону этого решения, и в целом это был очень интересный опыт. Большое спасибо, что ты, читатель, разделил его со мной, дойдя до этого момента.
UPD: Я не призываю кого-либо использовать этот сервер на постоянной основе. Несмотря на то, что он полностью меня устраивает, в нём нет большого количества нужных для обычного веб-сервера функций и не оптимизирован некоторый готовый функционал, вроде внятного определения mime-типов. Это всё будет в следующих статьях.
Создание простого веб-сервера с помощью Node.js и Express
Node.js с Express — это популярный дуэт, используемый многими приложениями во всем мире. Данный урок познакомит вас с функциональностью этих инструментов на примере сборки простого веб-сервера.
Создаваемый сервер будет обслуживать HTML-страницу, доступную другим людям. К концу статьи вы усвоите базовые знания о:
- Node.js;
- Express;
- npm;
- создании маршрутов Express;
- обслуживании HTML;
- настройке статических ресурсов Express.
Совет: не копируйте код урока, а напишите его сами. Это позволит вам лучше его понять и усвоить.
Создание и инициализация проекта
Первым шагом будет создать пустой каталог для проекта. Это можно сделать обычным способом либо из терминала с помощью следующих команд:
mkdir express-server
cd express-server
После создания проекта нужно его инициализировать:
npm init -y
Эта команда создает файл package.json и инициализирует его с предустановленными значениями. Если вы захотите сами заполнить его поля, удалите флаг -y и следуйте инструкциям.
Добавление Express
После инициализации проекта Node.js мы переходим к следующему шагу — добавлению Express. Установка пакетов в Node.js выполняется командой npm install packageName .
Для добавления последней стабильной версии Express выполните:
npm install express
После установки Express файл package.json будет выглядеть так:
"name": "express-server",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": "test": "echo \"Error: no test specified\" && exit 1"
>,
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": "express": "^4.17.1"
>
>
Express перечислен среди dependencies , значит, он установился успешно. Переходим к третьему шагу — созданию сервера.
Создание сервера Express
Прежде чем продолжать, нужно создать для сервера JS-файл. Выполните в терминале следующую команду:
touch index.js
Теперь откройте этот файл и пропишите в нем:
const express = require('express');
const app = express();
Что эти строки делают?
- Первая импортирует Express в проект, чтобы его можно было использовать. При каждом добавлении в проект пакета необходимо импортировать его туда, где он будет использоваться.
- Вторая строка вызывает функцию express , которая создает новое приложение, после чего присваивает результат константе app .
Создание маршрутов и прослушивание порта
Говоря простым языком, маршрут представляет конечную точку, к которой могут обращаться пользователи. Он ассоциируется с HTTP-глаголом (например, GET, POST и пр.) и получает путь URL. Кроме того, он получает функцию, которая вызывается при обращении к этой конечной точке.
Пропишите в файле следующий код:
app.get('/', (req, res) => res.send(< message: 'Hello WWW!' >);
>);
Разберем его согласно приведенной ранее структуре:
- Он связан с HTTP-глаголом — в данном случае GET.
- Он получает URL — здесь это домашняя страница ( / ).
- Он получает функцию, которая будет вызываться при обращении к конечной точке.
Следовательно, когда пользователь выполняет запрос GET к домашней странице, т.е. localhost:3333 , вызывается стрелочная функция и отображается фраза “Hello WWW!”
Последним шагом подготовки сервера к работе будет настройка слушателя. Понадобится указать для приложения конкретный порт. Напишите нижеприведенный код в конец JS-файла.
app.listen(3333, () => console.log('Application listening on port 3333!');
>);
Чтобы иметь возможность запускать сервер, вам нужно будет вызывать метод listen . При этом вы также можете изменить номер порта (3333) на любой другой.
Доступ к приложению в браузере
Для запуска приложения выполните в терминале node index.js . Имейте в виду, что index.js — это произвольное имя, которое я выбрал для данного урока, так что можете назвать его app.js или как-угодно иначе.
Теперь, когда сервер запущен, можно обратиться к нему в браузере. Перейдите по адресу http://localhost:3333/ , перед вами должно отобразиться следующее сообщение:
Вы отлично справились с настройкой веб-сервера Node.js + Express. В следующем разделе мы настроим статическое содержимое, а именно JavaScript, CSS, HTML, изображения и т.д.
Статические файлы
Наше приложение пока что выглядит не очень. Почему бы не добавить ему структуру и стилизацию? Но куда все это нужно добавлять?
В этом разделе вы узнаете, как настраивать и передавать статические ресурсы, такие как HTML, JavaScript, CSS и изображения.
Импорт модуля path
Первым делом нужно импортировать в приложение модуль path . Устанавливать ничего не нужно, потому что path предустановлен в Node изначально.
Пропишите в начале файла эту строку:
const path = require('path');
Зачем вообще этот модуль? Он позволяет генерировать абсолютные пути, которые необходимы для передачи статических файлов. Добавьте следующую строку в приложение перед определением маршрутов:
app.use(express.static(path.join(__dirname, 'public')));
path.join получает два аргумента:
- Текущую рабочую директорию (cwd).
- Вторую директорию, которую нужно объединить с cwd.
В качестве упражнения попробуйте вывести в консоль path.join(__dirname, ‘public’) и посмотрите, что получится.
На данный момент сервер должен выглядеть так:
const path = require('path');
const express = require('express');
const app = express();app.use(express.static(path.join(__dirname, 'public')))app.get('/', (req, res) => res.send(< message: 'Hello WWW!' >);
>);app.listen(3333, () => console.log('Application listening on port 3333!');
>);
Создание каталога public и добавление ресурсов
Создайте каталог и перейдите в него с помощью следующих команд:
mkdir public
cd public
Теперь создадим пустые файлы, куда затем добавим HTML, CSS и JavaScript. Выполните в терминале следующие команды:
touch app.js
touch index.html
touch styles.css
Мы оставим app.js максимально простым, добавив только сообщение, подтверждающее, что он работает:
alert('it works');
То же и с styles.css . Для проверки его работоспособности мы установим цвет фона на синий:
html background-color: blue;
>
В завершении нужно написать HTML, чтобы отображать все это на домашней странице. Откройте index.js и добавьте следующий HTML-код:
My server
My server
Server built with Node.js and Express
Теперь остается всего один шаг.
Передача HTML-файла
Мы почти закончили. Осталось только обработать HTML-код. Для этого нужно перейти в файл index.js и прописать в нем следующее:
app.get('/', (req, res) => res.sendFile(`$/public/index.html`);
>);
Если вы уже ранее работали с Node.js и Express, то можете спросить: “Что здесь делает метод sendFile и почему мы не используем render ?” Метод render мы использовать не можем, так как не задействуем никакой движок (например, Pug, EJS и т.д.). Следовательно, когда кто-либо обращается к домашней странице, мы отправляем назад HTML-файл.
Итоговый код сервера должен выглядеть так:
const path = require('path');
const express = require('express');
const app = express();app.use(express.static(path.join(__dirname, 'public')))app.get('/', (req, res) => res.sendFile(`$/public/index.html`);
>);app.listen(3333, () => console.log('Application listening on port 3333!');
>);
COPY
Если теперь вы перейдете по http://localhost:3333 , то увидите домашнюю страницу с синим фоном. Естественно, сначала нужно будет закрыть надоедливое всплывающее окошко.
Заключение
К завершению статьи у вас должно получиться простое веб-приложение.
В этом уроке мы узнали:
- о Node.js;
- об Express и о том, как использовать его для создания небольшого веб-приложения;
- как создавать маршруты;
- как настраивать статическое содержимое в приложении Node.js + Express;
- как передавать простой HTML-файл в Express.
- Создаем Telegram-бота с помощью Node.js за 3 минуты
- Найти и обезвредить: утечки памяти в Node.js
- Создание многопользовательской игры с использованием Socket.io при помощи NodeJS и React
How To Create a Web Server in Node.js with the HTTP Module
When you view a webpage in your browser, you are making a request to another computer on the internet, which then provides you the webpage as a response. That computer you are talking to via the internet is a web server. A web server receives HTTP requests from a client, like your browser, and provides an HTTP response, like an HTML page or JSON from an API.
A lot of software is involved for a server to return a webpage. This software generally falls into two categories: frontend and backend. Front-end code is concerned with how the content is presented, such as the color of a navigation bar and the text styling. Back-end code is concerned with how data is exchanged, processed, and stored. Code that handles network requests from your browser or communicates with the database is primarily managed by back-end code.
Node.js allows developers to use JavaScript to write back-end code, even though traditionally it was used in the browser to write front-end code. Having both the frontend and backend together like this reduces the effort it takes to make a web server, which is a major reason why Node.js is a popular choice for writing back-end code.
In this tutorial, you will learn how to build web servers using the http module that’s included in Node.js. You will build web servers that can return JSON data, CSV files, and HTML web pages.
Simplify deploying Node.js applications with DigitalOcean App Platform. Deploy Node directly from GitHub in minutes.
Prerequisites
- Ensure that Node.js is installed on your development machine. This tutorial uses Node.js version 10.19.0. To install this on macOS or Ubuntu 18.04, follow the steps in How to Install Node.js and Create a Local Development Environment on macOS or the Installing Using a PPA section of How To Install Node.js on Ubuntu 18.04.
- The Node.js platform supports creating web servers out of the box. To get started, be sure you’re familiar with the basics of Node.js. You can get started by reviewing our guide on How To Write and Run Your First Program in Node.js.
- We also make use of asynchronous programming for one of our sections. If you’re not familiar with asynchronous programming in Node.js or the fs module for interacting with files, you can learn more with our article on How To Write Asynchronous Code in Node.js.
Step 1 — Creating a Basic HTTP Server
Let’s start by creating a server that returns plain text to the user. This will cover the key concepts required to set up a server, which will provide the foundation necessary to return more complex data formats like JSON.
First, we need to set up an accessible coding environment to do our exercises, as well as the others in the article. In the terminal, create a folder called first-servers :
Then enter that folder:
Now, create the file that will house the code:
Open the file in a text editor. We will use nano as it’s available in the terminal:
We start by loading the http module that’s standard with all Node.js installations. Add the following line to hello.js :
first-servers/hello.js
const http = require("http");
The http module contains the function to create the server, which we will see later on. If you would like to learn more about modules in Node.js, check out our How To Create a Node.js Module article.
Our next step will be to define two constants, the host and port that our server will be bound to:
first-servers/hello.js
. const host = 'localhost'; const port = 8000;
As mentioned before, web servers accept requests from browsers and other clients. We may interact with a web server by entering a domain name, which is translated to an IP address by a DNS server. An IP address is a unique sequence of numbers that identify a machine on a network, like the internet. For more information on domain name concepts, take a look at our An Introduction to DNS Terminology, Components, and Concepts article.
The value localhost is a special private address that computers use to refer to themselves. It’s typically the equivalent of the internal IP address 127.0.0.1 and it’s only available to the local computer, not to any local networks we’ve joined or to the internet.
The port is a number that servers use as an endpoint or “door” to our IP address. In our example, we will use port 8000 for our web server. Ports 8080 and 8000 are typically used as default ports in development, and in most cases developers will use them rather than other ports for HTTP servers.
When we bind our server to this host and port, we will be able to reach our server by visiting http://localhost:8000 in a local browser.
Let’s add a special function, which in Node.js we call a request listener. This function is meant to handle an incoming HTTP request and return an HTTP response. This function must have two arguments, a request object and a response object. The request object captures all the data of the HTTP request that’s coming in. The response object is used to return HTTP responses for the server.
We want our first server to return this message whenever someone accesses it: «My first server!» .
Let’s add that function next:
first-servers/hello.js
. const requestListener = function (req, res) res.writeHead(200); res.end("My first server!"); >;
The function would usually be named based on what it does. For example, if we created a request listener function to return a list of books, we would likely name it listBooks() . Since this one is a sample case, we will use the generic name requestListener .
All request listener functions in Node.js accept two arguments: req and res (we can name them differently if we want). The HTTP request the user sends is captured in a Request object, which corresponds to the first argument, req . The HTTP response that we return to the user is formed by interacting with the Response object in second argument, res .
The first line res.writeHead(200); sets the HTTP status code of the response. HTTP status codes indicate how well an HTTP request was handled by the server. In this case, the status code 200 corresponds to «OK» . If you are interested in learning about the various HTTP codes that your web servers can return with the meaning they signify, our guide on How To Troubleshoot Common HTTP Error Codes is a good place to start.
The next line of the function, res.end(«My first server!»); , writes the HTTP response back to the client who requested it. This function returns any data the server has to return. In this case, it’s returning text data.
Finally, we can now create our server and make use of our request listener:
first-servers/hello.js
. const server = http.createServer(requestListener); server.listen(port, host, () => console.log(`Server is running on http://$host>:$port>`); >);
Save and exit nano by pressing CTRL+X .
In the first line, we create a new server object via the http module’s createServer() function. This server accepts HTTP requests and passes them on to our requestListener() function.
After we create our server, we must bind it to a network address. We do that with the server.listen() method. It accepts three arguments: port , host , and a callback function that fires when the server begins to listen.
All of these arguments are optional, but it is a good idea to explicitly state which port and host we want a web server to use. When deploying web servers to different environments, knowing the port and host it is running on is required to set up load balancing or a DNS alias.
The callback function logs a message to our console so we can know when the server began listening to connections.
Note: Even though requestListener() does not use the req object, it must still be the first argument of the function.
With less than fifteen lines of code, we now have a web server. Let’s see it in action and test it end-to-end by running the program:
In the console, we will see this output:
OutputServer is running on http://localhost:8000
Notice that the prompt disappears. This is because a Node.js server is a long running process. It only exits if it encounters an error that causes it to crash and quit, or if we stop the Node.js process running the server.
In a separate terminal window, we’ll communicate with the server using cURL, a CLI tool to transfer data to and from a network. Enter the command to make an HTTP GET request to our running server:
When we press ENTER , our terminal will show the following output:
OutputMy first server!
We’ve now set up a server and got our first server response.
Let’s break down what happened when we tested our server. Using cURL, we sent a GET request to the server at http://localhost:8000 . Our Node.js server listened to connections from that address. The server passed that request to the requestListener() function. The function returned text data with the status code 200 . The server then sent that response back to cURL, which displayed the message in our terminal.
Before we continue, let’s exit our running server by pressing CTRL+C . This interrupts our server’s execution, bringing us back to the command line prompt.
In most web sites we visit or APIs we use, the server responses are seldom in plain text. We get HTML pages and JSON data as common response formats. In the next step, we will learn how to return HTTP responses in common data formats we encounter in the web.
Step 2 — Returning Different Types of Content
The response we return from a web server can take a variety of formats. JSON and HTML were mentioned before, and we can also return other text formats like XML and CSV. Finally, web servers can return non-text data like PDFs, zipped files, audio, and video.
In this article, in addition to the plain text we just returned, you’ll learn how to return the following types of data:
The three data types are all text-based, and are popular formats for delivering content on the web. Many server-side development languages and tools have support for returning these different data types. In the context of Node.js, we need to do two things:
- Set the Content-Type header in our HTTP responses with the appropriate value.
- Ensure that res.end() gets the data in the right format.
Let’s see this in action with some examples. The code we will be writing in this section and later ones have many similarities to the code we wrote previously. Most changes exist within the requestListener() function. Let’s create files with this “template code” to make future sections easier to follow.
Create a new file called html.js . This file will be used later to return HTML text in an HTTP response. We’ll put the template code here and copy it to the other servers that return various types.
In the terminal, enter the following:
Now open this file in a text editor:
Let’s copy the “template code.” Enter this in nano :
first-servers/html.js
const http = require("http"); const host = 'localhost'; const port = 8000; const requestListener = function (req, res) >; const server = http.createServer(requestListener); server.listen(port, host, () => console.log(`Server is running on http://$host>:$port>`); >);
Save and exit html.js with CTRL+X , then return to the terminal.
Now let’s copy this file into two new files. The first file will be to return CSV data in the HTTP response:
The second file will return a JSON response in the server:
The remaining files will be for later exercises:
We’re now set up to continue our exercises. Let’s begin with returning JSON.
Serving JSON
JavaScript Object Notation, commonly referred to as JSON, is a text-based data exchange format. As its name suggests, it is derived from JavaScript objects, but it is language independent, meaning it can be used by any programming language that can parse its syntax.
JSON is commonly used by APIs to accept and return data. Its popularity is due to lower data transfer size than previous data exchange standards like XML, as well as the tooling that exists that allow programs to parse them without excessive effort. If you’d like to learn more about JSON, you can read our guide on How To Work with JSON in JavaScript.
Open the json.js file with nano :
We want to return a JSON response. Let’s modify the requestListener() function to return the appropriate header all JSON responses have by changing the highlighted lines like so:
first-servers/json.js
. const requestListener = function (req, res) res.setHeader("Content-Type", "application/json"); >; .
The res.setHeader() method adds an HTTP header to the response. HTTP headers are additional information that can be attached to a request or a response. The res.setHeader() method takes two arguments: the header’s name and its value.
The Content-Type header is used to indicate the format of the data, also known as media type, that’s being sent with the request or response. In this case our Content-Type is application/json .
Now, let’s return JSON content to the user. Modify json.js so it looks like this:
first-servers/json.js
. const requestListener = function (req, res) res.setHeader("Content-Type", "application/json"); res.writeHead(200); res.end(` `); >; .
Like before, we tell the user that their request was successful by returning a status code of 200 . This time in the response.end() call, our string argument contains valid JSON.
Save and exit json.js by pressing CTRL+X . Now, let’s run the server with the node command:
In another terminal, let’s reach the server by using cURL:
As we press ENTER , we will see the following result:
Output
We now have successfully returned a JSON response, just like many of the popular APIs we create apps with. Be sure to exit the running server with CTRL+C so we can return to the standard terminal prompt. Next, let’s look at another popular format of returning data: CSV.
Serving CSV
The Comma Separated Values (CSV) file format is a text standard that’s commonly used for providing tabular data. In most cases, each row is separated by a newline, and each item in the row is separated by a comma.
In our workspace, open the csv.js file with a text editor:
Let’s add the following lines to our requestListener() function:
first-servers/csv.js
. const requestListener = function (req, res) res.setHeader("Content-Type", "text/csv"); res.setHeader("Content-Disposition", "attachment;filename=oceanpals.csv"); >; .
This time, our Content-Type indicates that a CSV file is being returned as the value is text/csv . The second header we add is Content-Disposition . This header tells the browser how to display the data, particularly in the browser or as a separate file.
When we return CSV responses, most modern browsers automatically download the file even if the Content-Disposition header is not set. However, when returning a CSV file we should still add this header as it allows us to set the name of the CSV file. In this case, we signal to the browser that this CSV file is an attachment and should be downloaded. We then tell the browser that the file’s name is oceanpals.csv .
Let’s write the CSV data in the HTTP response:
first-servers/csv.js
. const requestListener = function (req, res) res.setHeader("Content-Type", "text/csv"); res.setHeader("Content-Disposition", "attachment;filename=oceanpals.csv"); res.writeHead(200); res.end(`id,name,email\n1,Sammy Shark,shark@ocean.com`); >; .
Like before we return a 200 / OK status with our response. This time, our call to res.end() has a string that’s a valid CSV. The comma separates the value in each column and the new line character ( \n ) separates the rows. We have two rows, one for the table header and one for the data.
We’ll test this server in the browser. Save csv.js and exit the editor with CTRL+X .
Run the server with the Node.js command:
In another Terminal, let’s reach the server by using cURL:
The console will show this:
Outputid,name,email 1,Sammy Shark,shark@ocean.com
If we go to http://localhost:8000 in our browser, a CSV file will be downloaded. Its file name will be oceanpals.csv .
Exit the running server with CTRL+C to return to the standard terminal prompt.
Having returned JSON and CSV, we’ve covered two cases that are popular for APIs. Let’s move on to how we return data for websites people view in a browser.
Serving HTML
HTML, HyperText Markup Language, is the most common format to use when we want users to interact with our server via a web browser. It was created to structure web content. Web browsers are built to display HTML content, as well as any styles we add with CSS, another front-end web technology that allows us to change the aesthetics of our websites.
Let’s reopen html.js with our text editor:
Modify the requestListener() function to return the appropriate Content-Type header for an HTML response:
first-servers/html.js
. const requestListener = function (req, res) res.setHeader("Content-Type", "text/html"); >; .
Now, let’s return HTML content to the user. Add the highlighted lines to html.js so it looks like this:
first-servers/html.js
. const requestListener = function (req, res) res.setHeader("Content-Type", "text/html"); res.writeHead(200); res.end(`This is HTML
`); >; .
We first add the HTTP status code. We then call response.end() with a string argument that contains valid HTML. When we access our server in the browser, we will see an HTML page with one header tag containing This is HTML .
Let’s save and exit by pressing CTRL+X . Now, let’s run the server with the node command:
We will see Server is running on http://localhost:8000 when our program has started.
Now go into the browser and visit http://localhost:8000 . Our page will look like this:

Let’s quit the running server with CTRL+C and return to the standard terminal prompt.
It’s common for HTML to be written in a file, separate from the server-side code like our Node.js programs. Next, let’s see how we can return HTML responses from files.
Step 3 — Serving an HTML Page From a File
We can serve HTML as strings in Node.js to the user, but it’s preferable that we load HTML files and serve their content. This way, as the HTML file grows we don’t have to maintain long strings in our Node.js code, keeping it more concise and allowing us to work on each aspect of our website independently. This “separation of concerns” is common in many web development setups, so it’s good to know how to load HTML files to support it in Node.js
To serve HTML files, we load the HTML file with the fs module and use its data when writing our HTTP response.
First, we’ll create an HTML file that the web server will return. Create a new HTML file:
Now open index.html in a text editor:
Our web page will be minimal. It will have an orange background and will display some greeting text in the center. Add this code to the file:
first-servers/index.html
DOCTYPE html> head> title>My Websitetitle> style> *, html margin: 0; padding: 0; border: 0; > html width: 100%; height: 100%; > body width: 100%; height: 100%; position: relative; background-color: rgb(236, 152, 42); > .center width: 100%; height: 50%; margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: white; font-family: "Trebuchet MS", Helvetica, sans-serif; text-align: center; > h1 font-size: 144px; > p font-size: 64px; > style> head> body> div class="center"> h1>Hello Again!h1> p>This is served from a filep> div> body> html>
This single webpage shows two lines of text: Hello Again! and This is served from a file . The lines appear in the center of the page, one above each other. The first line of text is displayed in a heading, meaning it would be large. The second line of text will appear slightly smaller. All the text will appear white and the webpage has an orange background.
While it’s not the scope of this article or series, if you are interested in learning more about HTML, CSS, and other front-end web technologies, you can take a look at Mozilla’s Getting Started with the Web guide.
That’s all we need for the HTML, so save and exit the file with CTRL+X . We can now move on to the server code.
For this exercise, we’ll work on htmlFile.js . Open it with the text editor:
As we have to read a file, let’s begin by importing the fs module:
first-servers/htmlFile.js
const http = require("http"); const fs = require('fs').promises; .
This module contains a readFile() function that we’ll use to load the HTML file in place. We import the promise variant in keeping with modern JavaScript best practices. We use promises as its syntactically more succinct than callbacks, which we would have to use if we assigned fs to just require(‘fs’) . To learn more about asynchronous programming best practices, you can read our How To Write Asynchronous Code in Node.js guide.
We want our HTML file to be read when a user requests our system. Let’s begin by modifying requestListener() to read the file:
first-servers/htmlFile.js
. const requestListener = function (req, res) fs.readFile(__dirname + "/index.html") >; .
We use the fs.readFile() method to load the file. Its argument has __dirname + «/index.html» . The special variable __dirname has the absolute path of where the Node.js code is being run. We then append /index.html so we can load the HTML file we created earlier.
Now let’s return the HTML page once it’s loaded:
first-servers/htmlFile.js
. const requestListener = function (req, res) fs.readFile(__dirname + "/index.html") .then(contents => res.setHeader("Content-Type", "text/html"); res.writeHead(200); res.end(contents); >) >; .
If the fs.readFile() promise successfully resolves, it will return its data. We use the then() method to handle this case. The contents parameter contains the HTML file’s data.
We first set the Content-Type header to text/html to tell the client that we are returning HTML data. We then write the status code to indicate the request was successful. We finally send the client the HTML page we loaded, with the data in the contents variable.
The fs.readFile() method can fail at times, so we should handle this case when we get an error. Add this to the requestListener() function:
first-servers/htmlFile.js
. const requestListener = function (req, res) fs.readFile(__dirname + "/index.html") .then(contents => res.setHeader("Content-Type", "text/html"); res.writeHead(200); res.end(contents); >) .catch(err => res.writeHead(500); res.end(err); return; >); >; .
Save the file and exit nano with CTRL+X .
When a promise encounters an error, it is rejected. We handle that case with the catch() method. It accepts the error that fs.readFile() returns, sets the status code to 500 signifying that an internal error was encountered, and returns the error to the user.
Run our server with the node command:
In the web browser, visit http://localhost:8000 . You will see this page:

You have now returned an HTML page from the server to the user. You can quit the running server with CTRL+C . You will see the terminal prompt return when you do.
When writing code like this in production, you may not want to load an HTML page every time you get an HTTP request. While this HTML page is roughly 800 bytes in size, more complex websites can be megabytes in size. Large files can take a while to load. If your site is expecting a lot of traffic, it may be best to load HTML files at startup and save their contents. After they are loaded, you can set up the server and make it listen to requests on an address.
To demonstrate this method, let’s see how we can rework our server to be more efficient and scalable.
Serving HTML Efficiently
Instead of loading the HTML for every request, in this step we will load it once at the beginning. The request will return the data we loaded at startup.
In the terminal, re-open the Node.js script with a text editor:
Let’s begin by adding a new variable before we create the requestListener() function:
first-servers/htmlFile.js
. let indexFile; const requestListener = function (req, res) .
When we run this program, this variable will hold the HTML file’s contents.
Now, let’s readjust the requestListener() function. Instead of loading the file, it will now return the contents of indexFile :
first-servers/htmlFile.js
. const requestListener = function (req, res) res.setHeader("Content-Type", "text/html"); res.writeHead(200); res.end(indexFile); >; .
Next, we shift the file reading logic from the requestListener() function to our server startup. Make the following changes as we create the server:
first-servers/htmlFile.js
. const server = http.createServer(requestListener); fs.readFile(__dirname + "/index.html") .then(contents => indexFile = contents; server.listen(port, host, () => console.log(`Server is running on http://$host>:$port>`); >); >) .catch(err => console.error(`Could not read index.html file: $err>`); process.exit(1); >);
Save the file and exit nano with CTRL+X .
The code that reads the file is similar to what we wrote in our first attempt. However, when we successfully read the file we now save the contents to our global indexFile variable. We then start the server with the listen() method. The key thing is that the file is loaded before the server is run. This way, the requestListener() function will be sure to return an HTML page, as indexFile is no longer an empty variable.
Our error handler has changed as well. If the file can’t be loaded, we capture the error and print it to our console. We then exit the Node.js program with the exit() function without starting the server. This way we can see why the file reading failed, address the problem, and then start the server again.
We’ve now created different web servers that return various types of data to a user. So far, we have not used any request data to determine what should be returned. We’ll need to use request data when setting up different routes or paths in a Node.js server, so next let’s see how they work together.
Step 4 — Managing Routes Using an HTTP Request Object
Most websites we visit or APIs we use usually have more than one endpoint so we can access various resources. A good example would be a book management system, one that might be used in a library. It would not only need to manage book data, but it would also manage author data for cataloguing and searching convenience.
Even though the data for books and authors are related, they are two different objects. In these cases, software developers usually code each object on different endpoints as a way to indicate to the API user what kind of data they are interacting with.
Let’s create a new server for a small library, which will return two different types of data. If the user goes to our server’s address at /books , they will receive a list of books in JSON. If they go to /authors , they will receive a list of author information in JSON.
So far, we have been returning the same response to every request we get. Let’s illustrate this quickly.
Re-run our JSON response example:
In another terminal, let’s do a cURL request like before:
