Polkadot
Features
-
Intentionally Minimal
Build your own stack! Polkadot doesn't even include a router.
Polkadot can accommodate any & all of your preferences. This is your app. -
Extremely Lightweight
Even with all dependencies, Polkadot weighs less than 15kB!
It's the perfect candidate for serverless environments. -
Simple yet Flexible
You can learn the "framework" during lunch and deploy before the day's end.
There's no limit to what you can do β nor how you can do it. -
Highly Performant
Because Polkadot does so little, it's as "blazing fast" as they come. -
Async Support
It's the 2020s β time toawait
all the things~!
Install
$ npm install --save polkadot
Usage: CLI
Just specify an entry file β that's it! π
If you do not provide one, then index.js
is assumed.
Customize the port by setting the PORT
environment variable.
The PORT
will default to 3000
if left undeclared.
Important: An error will be thrown if the
PORT
is in use.
# Examples:
$ polkadot
$ polkadot app.js
$ PORT=8080 polkadot app.js
// index.js
const { get } = require('httpie');
module.exports = async (req, res) => {
let uri = `https://jsonplaceholder.typicode.com/posts`;
if (req.query.id) {
uri += `/${req.query.id}`;
}
let res = await get(uri);
return res.data;
}
Usage: Programmatic
For those who need to control the lifecycle and/or lifespan of their server(s), a programmatic API is available.
Similarly, this is the only way to customize the underlying server
itself (see the with-https
example).
const { get } = require('httpie');
const polkadot = require('polkadot');
const app = polkadot(async (req, res) => {
let uri = `https://jsonplaceholder.typicode.com/posts`;
if (req.query.id) {
uri += `/${req.query.id}`;
}
let res = await get(uri);
return res.data;
});
app.listen(3000, err => {
if (err) throw err;
console.log('> Running on localhost:3000');
});
API
Note: The following pertains to Programmatic Usage only
polkadot.handler(req, res)
Returns: Function
The main polkadot
handler.
It parses the req
(see IncomingMessage
) and assigns value to the path
, query
, and search
keys1.
It also waits until the res
(see ServerResponse
) has been terminated or until data is returned.2
1 See
@polka/url
for further details.
2 See Handlers for varying return types.
polkadot.listen()
Returns: http.Server
Boots (or creates) the underlying http.Server
for the first time.
All arguments are passed directly to server.listen
with no changes.
Handlers
Every handler receives a req
(IncomingMessage
) and a res
(ServerResponse
) pair of arguments.
These are the true, mostly unfettered1 instances that the http.Server
created, so you have full access to all native APIs.
1 Before calling your handler(s),
@polka/url
assigns value toreq.path
,req.query
, andreq.search
keys.
There are multiple ways to format or return the server response. You may mix and match them, as you are not restricted to a particular format in your application(s).
1. Use Native APIs
Because you have direct access to res
(see ServerResponse
), you can set headers, the statusCode, and/or write response data with the core Node.js methods:
module.exports = function (req, res) {
res.statusCode = 400;
res.setHeader('Content-Type', 'application/json');
res.end('{"error":"Bad Request"}');
}
@polka/send-type
library
2. The The @polka/send-type
library is a utility function that composes your response through a simple API. It also inspects your response data and will auto-set its Content-Type
(if unspecified) and Content-Length
headers for you. Additionally, it will stringify Objects into JSON on your behalf!
Note: Check out its Data Detections documentation.
Because the @polka/send-type
library is already a dependency of polkadot
, using it comes at no extra cost!
const send = require('@polka/send-type');
module.exports = function (req, res) {
send(res, 400, {
error: 'Bad Request'
});
}
3. Return data
Polka uses the @polka/send-type
library internally, which allows you to return
data directly from your function handler instead of using the native APIs to format the response manually.
Because of this, @polka/send-type
can inspect your outgoing data and determine its Content-Type
(if unspecified) and Content-Length
response headers on your behalf. Similarly, it will automatically convert Objects into JSON strings.
Note: Check out its Data Detections documentation.
module.exports = function (req, res) {
res.statusCode = 400;
return {
error: 'Bad Request'
};
};
4. Async Returns
Polkadot works great with asynchronous functions!
You can certainly fetch data from external APIs, interact with databases, ...etc without any problems.
Of course, your asynchronous chain(s) may also use native res
APIs, the @polka/send-type
helper, or may return data directly. All options are always available!
The only rule is that if your handler ends in a Promise
or AsyncFunction
, that function must be returned so that Polkadot can resolve it on your behalf.
Important: The use of
AsyncFunction
is only supported in Node versions7.4
and above.
// For demo, not required
const send = require('@polka/send-type');
// Using Promises
module.exports = function (req, res) {
// must `return` the Promise
return isUser(req).then(user => {
if (user) {
send(res, 200, { user });
} else {
send(res, 401, 'You must be logged in');
}
});
};
// Using AsyncFunctions
module.exports = async function (req, res) {
const user = await isUser(req);
if (user) {
send(res, 200, { user });
} else {
send(res, 401, 'You must be logged in');
}
}
Routing
Before your handler is called, Polkadot will parse the request to provide you with some core information.
It will use @polka/url
to make req.path
, req.search
, and req.query
available.
While polkadot does not include a router, you can create your own or import any router of your choosing!
Please check out the with-router
example that uses Trouter to build a full JSON resource.
Below is a simple example that serves images & video files based on the incoming path:
const fs = require('fs');
const { join } = require('path');
const mime = require('mime/lite');
const assets = join(__dirname, 'assets');
function sendfile(res, dir, filename) {
const file = join(assets, dir, filename);
if (fs.existsSync(file)) {
res.setHeader('Content-Type', mime.getType(file));
fs.createReadStream(file).pipe(res);
} else {
res.statusCode = 404;
res.end('File not found');
}
}
// Supports: /images?filename=foobar.jpg
// Supports: /videos?filename=foobar.mp4
module.exports = function (req, res) {
const { filename } = req.query;
if (req.path === '/images') {
sendfile(res, 'images', filename);
} else if (req.path === '/videos') {
sendfile(res, 'videos', filename);
} else {
res.statusCode = 404;
res.end('Unknown filetype');
}
}
Error Handling
You must handle your own errors. This is because Polkadot will not dictate your application design nor its behavior βΒ and how an application responds to and handles errors is a large, important part of its design!
Please visit the
with-middleware
or thewith-router
examples.
They are both more complex demonstrations that handle errors while chaining or composing functions together.
For simple endpoints, it's very straightforward (as it should be):
// Send 404 if unknown path
module.exports = function (req, res) {
if (req.path !== '/') {
res.statusCode = 404;
return 'Page not found';
}
return 'OK';
}
// ---
const { get } = require('httpie');
// Send 404 if ID unknown to external API
module.exports = async function (req, res) {
try {
const ID = req.query.id;
let { data } = await get(`https://example.com/users/${ID}`);
send(res, 200, { user:data });
} catch (err) {
const code = err.statusCode || 404;
const message = err.data || 'User not found';
console.error('Error: ', req.query.id, code, message);
send(res, code, message);
}
}
Benchmarks
For performance results and comparisons, please check out the bench
directory.
Prior Art
Polkadot is the "little sibling" to Polka. It's effectively the core of Polka, stripped of all routing, middleware sequencing, and Express compatibility layers. While Polka is already leaner than most everything else, there was an opportunity to further satisfy the minimalists and make a microscopic version of Polka β hence, polkaβ’dot.
Additionally, Polkadot follows in the footsteps of micro
, which was the first HTTP framework of our kind (to my knowledge).
License
MIT Β© Luke Edwards