UA
UAH

Простий REST API на Node.js: GET/POST/DELETE

Що робить приклад

Blog Ендпоінти:

  • GET /api/items — список нотаток/задач
  • POST /api/items — створити запис
  • DELETE /api/items/:id — видалити запис за ID

Зберігання: файл data.json (підходить для shared).
Безпека: rate-limit, проста перевірка токена (опціонально).
Сервісні маршрути: / health (перевірка), / (коротка підказка).

Структура проекту

rest-api/
+-- package.json
+-- app.js
+-- storage.js # робота з файлом data.json
+-- data.json # створиться автоматично при першому запуску
+-- .env # опціонально для локального запуску

package.json


{
  "name": "rest-api",
  "version": "1.0.0",
  "description": "Simple REST API (notes/todos) for shared hosting (ISPmanager).",
  "main": "app.js",
  "scripts": {
    "start": "node app.js",
    "start:prod": "NODE_ENV=production node app.js"
  },
  "engines": {
    "node": ">=18.x"
  },
  "dependencies": {
    "dotenv": "^16.4.5",
    "express": "^4.19.2",
    "express-rate-limit": "^7.4.0",
    "helmet": "^7.1.0",
    "morgan": "^1.10.0",
    "nanoid": "^5.0.7"
  }
}
	
	

storage.js — міні-сховище у файлі


// storage.js
const fs = require('fs');
const path = require('path');

const DATA_PATH = path.join(__dirname, 'data.json');

function readAll() {
  if (!fs.existsSync(DATA_PATH)) {
    fs.writeFileSync(DATA_PATH, JSON.stringify([], null, 2));
  }
  const raw = fs.readFileSync(DATA_PATH, 'utf8');
  try { return JSON.parse(raw); } catch {
    // якщо файл пошкоджено - перезапишемо порожнім масивом
    fs.writeFileSync(DATA_PATH, JSON.stringify([], null, 2));
    return [];
  }
}

function writeAll(items) {
  fs.writeFileSync(DATA_PATH, JSON.stringify(items, null, 2));
}

module.exports = { readAll, writeAll, DATA_PATH };

	
	

app.js — сервер та маршрути API


// app.js
require('dotenv').config();
const express = require('express');
const helmet = require('helmet');
const morgan = require('morgan');
const rateLimit = require('express-rate-limit');
const { nanoid } = require('nanoid');
const { readAll, writeAll } = require('./storage');

const app = express();
const PORT = process.env.PORT || 3000;
const isProd = process.env.NODE_ENV === 'production';

// БЕЗПЕКА, ЛОГИ, ПАРСИНГ
app.use(helmet());
app.use(morgan(isProd ? 'combined' : 'dev'));
app.use(express.json()); // API приймає JSON

// ПРОСТИЙ CORS (якщо звертатиметеся з браузера)
app.use((req, res, next) => {
  const origin = req.headers.origin || '*';
  res.setHeader('Access-Control-Allow-Origin', origin);
  res.setHeader('Vary', 'Origin');
  res.setHeader('Access-Control-Allow-Methods', 'GET,POST,DELETE,OPTIONS');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  if (req.method === 'OPTIONS') return res.sendStatus(204);
  next();
});

// RATE-LIMIT (анти-спам)
const apiLimiter = rateLimit({
  windowMs: 60 * 1000,
  max: 60, // 60 запитів на хвилину на IP
  standardHeaders: true,
  legacyHeaders: false
});
app.use('/api/', apiLimiter);

// ОПЦІОНАЛЬНИЙ ТОКЕН (задайте API_TOKEN в оточенні)
// Приклад: Authorization: Bearer my-secret-token
function authIfEnabled(req, res, next) {
  const token = process.env.API_TOKEN;
  if (!token) return next(); // токен не налаштований - пропускаємо
  const header = req.headers.authorization || '';
  const provided = header.startsWith('Bearer ') ? header.slice(7) : '';
  if (provided && provided === token) return next();
  return res.status(401).json({ error: 'Unauthorized' });
}

// INDEX/HEALTH
app.get('/', (req, res) => {
  res.type('text/plain').send([
    'Simple REST API (notes/todos)',
    'GET    /api/items',
    'POST   /api/items   { "title": "...", "done": false }',
    'DELETE /api/items/:id',
    'GET    /health'
  ].join('\n'));
});

app.get('/health', (req, res) => res.json({ status: 'ok', uptime: process.uptime() }));

// ---- API ----

// Список
app.get('/api/items', authIfEnabled, (req, res) => {
  const items = readAll();
  res.json(items);
});

// Створення
app.post('/api/items', authIfEnabled, (req, res) => {
  const { title = '', done = false } = req.body || {};
  const t = String(title).trim();
  if (t.length < 1 || t.length > 200) {
    return res.status(400).json({ error: 'Title must be 1..200 chars' });
  }
  const items = readAll();
  const item = {
    id: nanoid(12),
    title: t,
    done: Boolean(done),
    createdAt: Date.now()
  };
  items.unshift(item); // додамо на початок
  writeAll(items);
  res.status(201).json(item);
});

// Видалення
app.delete('/api/items/:id', authIfEnabled, (req, res) => {
  const { id } = req.params;
  const items = readAll();
  const index = items.findIndex(i => i.id === id);
  if (index === -1) {
    return res.status(404).json({ error: 'Not found' });
  }
  const [removed] = items.splice(index, 1);
  writeAll(items);
  res.json({ removed });
});

// 404 и 500
app.use((req, res) => res.status(404).json({ error: 'Not Found' }));
app.use((err, req, res, next) => {
  console.error('Unhandled error:', err);
  res.status(500).json({ error: 'Internal Server Error' });
});

app.listen(PORT, () => {
  console.log(`REST API running on port ${PORT}`);
});
  
  

.env (для локального запуску)


PORT=3000
NODE_ENV=development

# Опціонально – включить захист токеном:
# API_TOKEN=my-secret-token
  
  

У продакшені (ISPmanager) задайте змінні в панелі. Файл .env не можна завантажувати.

Node.js
Node.js Хостинг
Запусти проект за пару кліків!
Безкоштовний SSLСучасні сервери7 днів тесту безкоштовно
Перейти до тарифів

Локальний запуск


npm install
npm start
# або
npm run start:prod
Перевірка:
curl http://localhost:3000/health
curl http://localhost:3000/api/items
curl -X POST http://localhost:3000/api/items \
  -H "Content-Type: application/json" \
  -d '{"title":"Перша замітка","done":false}'
curl -X DELETE http://localhost:3000/api/items/
Якщо увімкнули токен (API_TOKEN=...), додайте заголовок:
-H "Authorization: Bearer my-secret-token"
  
  

Депло в ISPmanager на shared-хостингу

  • Створіть домен/сайт у ISPmanager
  • Завантажте проект у каталог сайту
  • У терміналі/SSH: npm install
  • У розділі Node.js вкажіть:
    • Версію Node.js (LTS)
    • Старт: app.js або команда npm start
    • Змінні оточення (опціонально): NODE_ENV=production, API_TOKEN=...,
    • Прив'яжіть додаток до домену/піддомену (панель сама налаштує проксі)
  • Увімкніть HTTPS (Let's Encrypt) на домені
  • Перевірте: https://ваш-домен/api/items.

Готові приклади запитів (cURL)

Отримати список

curl -s https://ваш-домен/api/items

Додати запис

  
curl -s -X POST https://ваш-домен/api/items \
  -H "Content-Type: application/json" \
  -d '{"title":"Купити хостинг","done":false}'
    
  

Видалити запис


  curl -s -X DELETE https://ваш-домен/api/items/ID_З_СПИСКУ

Якщо увімкнено токен, додайте: -H "Authorization: Bearer my-secret-token".

Часті питання

Для міні-API, демо та MVP - так. Для серйозних проектів переходьте на БД (MySQL/PostgreSQL) або VPS.
Додайте маршрут PATCH /api/items/:id і змінюйте title/done за схемою, схожою з POST.
Зчитуйте параметри ?limit=…&offset=… (або page), повертайте total та шматок масиву.
Увімкніть токен, обмежте IP, додайте reCAPTCHA (якщо запити йдуть з браузера), використовуйте HTTPS.

Міні-чекіст продакшену

Програма слухає process.env.PORT.
Увімкнено HTTPS.
Настроєний rate-limit.
(Опціонально) Включено API_TOKEN та CORS під потрібні домени.
Робите бекап data.json.

Реквізити: Україна, 61202, Харків, пр. Людвіга Свободи 26/298.
ФО-П Харитінов Олег Сергійович
IBAN: UA073052990000026001005905889
МФО 305299
ІПН 2961615658
АТ КБ "ПриватБанк"
mail:
Документи:
Служба підтримки: телефон + 380 57 7209279
створити тікет

Виберіть мову

Українська
English
Русский