express-saga
v0.1.0
Published
express-saga is a web framework based on express and the saga pattern (redux-saga's interpretation).
Downloads
6
Maintainers
Readme
status: pre-alpha
express-saga is a web framework based on express and the saga pattern (redux-saga's interpretation).
You can use all existing express middleware without issue, but often there are better alternatives in express-saga.
Most effects from redux-saga are available, along with new effects for web server development.
This library is designed with a heavy focus on unit-testability. You don't need module level mocks due to dependency injection. Your routers are pure functions, so you don't need to run a http server in tests.
It also supports hot module replacement for far fewer in-development server restarts, without special build tools.
Install
npm install --save express express-saga
Example
First we'll set up a development server with hot-module-replacement. We're using mongo, but this will work with any database. This file only runs once, so create persistent objects here. When you change this file, restart the server.
server.js:
const express = requre('express');
const { MongoClient } = require('mongodb');
const hot = require('express-saga/hot');
const app = express();
function getSaga() {
const saga = require('./rootSaga');
return saga;
}
const mongo = new MongoClient();
mongo.connect(`mongodb://localhost:27017/myproject`).then((db) => {
const services = { todos: db.collection('todos') };
app.use(hot.saga(getSaga, services));
app.listen(process.env.PORT || 4000);
});
Then rootSaga.js is the main app. We build and export a root saga which delegates work to the other sagas.
const $ = require('express-saga');
const staticSaga = function* (req, res, next) {
const path = yield $.resolveStatic(`{__dirname}/public`, req.url);
if (!path) return next();
yield $.cors('*');
yield $.send.file(path);
yield $.end();
}
const getTodosSaga = function* () {
const todoService = yield $.service('todos');
// note that $.call takes a function; this aids in testing because
// you can choose to call the function or not in tests
const data = yield $.call(() => todoService.find({}).toArray());
// send back as json, defaults to 200 status code
yield $.send.json(data);
};
const createTodoSaga = function* () {
const todoService = yield $.service('todos');
// body parsing is done as-needed
// this ensures you only accept the format you expect
const body = yield $.body.json();
try {
yield $.call(() => todoService.create(body));
yield $.send.json({ success: true });
} catch (e) {
yield $.status(400);
yield $.send.json({ success: false });
}
};
const apiRouter = function* () {
yield $.get('/todos', getTodosSaga);
yield $.post('/todos', createTodoSaga);
};
const rootSaga = function*(req, res, next) {
const routes = [
$.get('*', staticSaga),
$.router('/api', apiRouter),
];
yield $.routes(routes, req, res, next);
}
module.exports = rootSaga;
Then run node server.js
, or env SAGA_DEBUG='*' node server.js
.