RESTful API с помощью Express и MassiveJS

249
views

RESTful api with MassiveJS and express

MassiveJS — это замечательная библиотека, которая позволяет легко делать прямые запросы из Node в базу PostgreSQL. Вместо того, чтобы пытаться преобразовывать данные базы в объекты, как это делает ORM, MassiveJS дает возможность работать напрямую с таблицами и функциями базы.

В этом посте, я расскажу как писать простое API для TODO — приложения с помощью Express и MassiveJS.

Создаем приложение с MassiveJS

Давайте начнем с базового express приложения:

npm install -g express-generator yarn
express todoApi
cd todoApi && yarn

Теперь создадим базу для нашего приложения и установим MassiveJS.

psql -c "CREATE ROLE todo_user \
  LOGIN PASSWORD 'todo_password' SUPERUSER;" template1
psql -c "CREATE DATABASE todo WITH OWNER todo_user;" template1
yarn add massive

Для использования данных для подключения к базе, мы воспользуемся dotenv модулем, который позволит считывать переменные нашего окружения из .env файла.

yarn add dotenv
dburl="postgres://todo_user:todo_password@localhost/todo" &&
echo "DATABASE_URL=${dburl}" > .env

Теперь мы можем подключить базу к нашему приложению:

# app.js
require('dotenv').config();
var express = require("express");
var app = express();
var massive = require("massive");
app.set('db', massive.connectSync({
  connectionString : process.env.DATABASE_URL
}));

Вам нет нужды беспокоиться об открытии/закрытии соединения для каждого запроса, MassiveJS позаботится об этом за вас.

Для миграций, мы воспользуемся модулем db-migrate, который мне нравится тем, что позволяет писать миграции на чистом SQL и не заставляет изучать новый синтаксис.

yarn add db-migrate db-migrate-pg --dev
db-migrate create add_todo_table --sql-file

Теперь мы можем редактировать файлы миграций для создания таблицы для нашего TODO — приложения.

# migrations/sqls/20161029163827-add-todo-table-up.sql
CREATE TABLE "todo" (
  "id" SERIAL NOT NULL PRIMARY KEY,
  "text" TEXT,
  "completedAt" TIMESTAMPTZ,
  "deletedAt" TIMESTAMPTZ
);

# 20161029163827-add-todo-table-down.sql
DROP table "todo";

Миграции могут быть запущены с помощью команд: db-migrate up и db-migrate down.

Добавляем API методы

С готовой таблицей можно приступить к написанию стандартных операций RESTful API.

var express = require('express');
var router = express.Router();
// INDEX todos
router.get('/', function(req, res, next){
  req.app.get('db').todo.find(
  function(err, result){
    if(err){ return next(err); }
    res.json(result);
  });
});
// CREATE a todo
router.post('/', function(req, res, next) {
  req.app.get('db').todo.save({
    text: req.body.text
  }, function(err, result){
    if(err){ return next(err); }
    res.status(201).json(result);
  });
});
// UPDATE a todo
router.put('/:id', function(req, res, next) {
  req.app.get('db').todo.save({
    id: req.params.id,
    text: req.body.text
  }, function(err, result){
    if(err){ return next(err); }
    else if(!result){
      return res.status(404).send('Not Found');
    }
    res.json(result);
  });
});
// DELETE a todo
router.delete('/:id', function(req, res, next) {
  req.app.get('db').todo.save({
    id: req.params.id,
    deletedAt: 'NOW()'
  }, function(err, result){
    if(err){ return next(err); }
    else if(!result){
      return res.status(404).send('Not Found');
    }
    res.json(result);
  });
});
module.exports = router;

Без определений каких-либо модулей, MassiveJS автоматически создаст db.todo объект, который будет предоставлять доступ к таблице todo. Он уже содержит несколько методов первой необходимости: insert, update, destroy, find, count, и т.д. (Даже поиск по тексту готов для нас).

Стоит обратить внимание, что метод save может как добавлять значение в таблицу, так и обновлять его, все зависит от контекста.

Это все замечательно, но мы до сих пор не воспользовались самым полезным инструментом MassiveJS: работа с SQL функциями напрямую.

Давайте предположим, что мы хотим добавлять пользователей в наше приложение. Важно, что мы не хотим сохранять пароли в открытом виде, поэтому будем использовать PostgreSQL расширение pgcrypto, которое шифрует пароли. (pgcrypto также позволяет использовать uuids, что тоже неплохо).

db-migrate create add-user-table --sql-file
# add-user-table-up.sql
-- enable pgcrypto
CREATE EXTENSION pgcrypto;
-- create user table
CREATE TABLE "user" (
  "id" UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY,
  "email" TEXT NOT NULL UNIQUE,
  "password" TEXT NOT NULL
);

# add-user-table-down.sql
-- remove user table
DROP TABLE "user";
-- disable pgcrypto
DROP EXTENSION pgcrypto;

Из коробки, MassiveJS будет читать любой файл .sql внутри папки базы, поэтому создадим следующие функции:

# db/createUser.sql
INSERT INTO "user"(email, password)
     VALUES (lower($1), crypt($2, gen_salt('bf', 8)))
  RETURNING id, email;

# db/findUserByCredentials
SELECT id, email
  FROM "user"
 WHERE email = lower($1)
       AND password = crypt($2, password);

И теперь мы можем добавить пару эндпоинтов для пользователей:

var express = require('express');
var router = express.Router();
// FIND a user by credentials
router.post('/sessions', function(req, res, next){
  req.app.get('db').findUserByCredentials([req.body.email, req.body.password],
  function(err, result){
    if(err){ return next(err); }
    else if(!result[0]){
      return res.status(404).send('Not Found');
    }
    // you could construct a JWT or a session here instead of
    // returning the user object
    res.status(201).json(result[0]);
  });
});
// CREATE a user
router.post('/', function(req, res, next){
  req.app.get('db').createUser([req.body.email, req.body.password],
  function(err, result){
    if(err){ return next(err); }
    res.status(201).json(result[0]);
  });
});
module.exports = router;

Вот и все. MassiveJS находит SQL файлы и делает их доступными как JS функции в объекте db.

Также стоит обратить внимание, что миграцию можно применить в самой базе. MassiveJS ее увидит.

Вывод

Прочитав данный пост, появляется ощущение как просто можно работать с Node и PostgreSQL с помощью библиотеки MassiveJS. Полное описание возможностей, можно просмотреть в официальной документации.

Если вам полезен данный материал, подписывайтесь на мой блог, чтобы получать уведомления о новых публикациях на темы: верстки, javascript и разработки кроссплатформенных мобильных приложений.

Подписаться на блог по эл. почте