Skip to content

Instantly share code, notes, and snippets.

@f5io f5io/core.js
Last active May 16, 2018

Embed
What would you like to do?
An approach to async middleware for raw `http` in Node.
const http = require('http');
const methods = [ 'get', 'put', 'post', 'delete', 'head' ];
const isStream = obj =>
obj &&
typeof obj === 'object' &&
typeof obj.pipe === 'function';
const isReadable = obj =>
isStream(obj) &&
typeof obj._read === 'function' &&
typeof obj._readableState === 'object';
const isNext = Symbol('isNext');
const composeWithContext = (...mw) => (...args) =>
new Promise(async (resolve, reject) => {
const nxt = args[args.length - 1][isNext] ? args.pop() : () => resolve();
await mw.reduceRight((next, curr) =>
async function() {
next[isNext] = true;
try { await curr(...args.concat(next)) }
catch(e) { reject(e); throw e; }
}, nxt)();
resolve();
});
const resolve = async (req, res, next) => {
await next();
if (isReadable(res.body)) {
res.body.pipe(res);
} else {
res.end(res.body || '');
}
};
const route = (path, fn) => async (req, res, next) => {
if (req.url === path) {
await composeWithContext(fn, next)(req, res);
} else {
await next();
}
};
const method = (me, fn) => async(req, res, next) => {
if (req.method.toLowerCase() === me) {
await composeWithContext(fn, next)(req, res);
} else {
await next();
}
};
const nomad = () => {
const mw = [ resolve ];
const app = {
use: (path, fn) => {
if (!fn && typeof path === 'function') return (mw.push(path), app);
if (typeof path === 'string' && typeof fn === 'function') return (mw.push(route(path, fn)), app);
},
listen: (port, callback = () => {}) => {
http.createServer(composeWithContext(...mw)).listen(port, callback);
}
};
return methods.reduce((a, me) => {
a[me] = (path, fn) => {
if (!fn && typeof path === 'function') return (mw.push(method(me, path)), app);
if (typeof path === 'string' && typeof fn === 'function') return (mw.push(route(path, method(me, fn))), app);
}
return a;
}, app);
};
export default nomad;
import fs from 'fs';
import nomad from './core';
const PORT = 1337;
let count = 0;
const stats = file =>
new Promise((resolve, reject) => {
fs.stat(file, (err, stat) => err ? reject(err) : resolve(stat));
});
const assets = path => async (req, res, next) => {
try {
const filePath = `${path}${req.url}`;
const stat = await stats(filePath);
if (!stat.isFile()) throw new Error('Cannot load directory');
res.statusCode = 200;
res.body = fs.createReadStream(filePath);
} catch(e) {
await next();
}
}
const app = nomad();
app.use(async (req, res, next) => {
const time = Date.now();
console.log(`initiate req ${++count}`);
await next();
res.setHeader('Response-Time', `${Date.now() - time}ms`);
});
app.use(async (req, res, next) => {
try { await next() }
catch(e) {
res.statusCode = 500;
res.body = e.message;
}
});
app.get(assets(process.cwd()));
app.use('/hello', (req, res, next) => {
res.statusCode = 200;
res.body = 'hello';
});
app.use('/world', (req, res, next) => {
res.statusCode = 200;
res.body = 'world';
});
app.get('/file', (req, res, next) => {
res.statusCode = 200;
res.body = fs.createReadStream(`${process.cwd()}/package.json`);
});
app.use(() => {
throw new Error('Route not found.');
});
app.listen(PORT, () => console.log(`Server started on: ${PORT}`));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.