An open API service indexing awesome lists of open source software.

https://github.com/lukeed/polkadot

The tiny HTTP server that gets out of your way! ・
https://github.com/lukeed/polkadot

Last synced: 5 months ago
JSON representation

The tiny HTTP server that gets out of your way! ・

Awesome Lists containing this project

README

          


polkadot

Polkadot



version


travis


codecov


downloads


install size

The tiny HTTP server that gets out of your way~!

## 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](#handlers)._

* **Highly Performant**

_Because Polkadot does so little, it's as ["blazing fast"](/bench) as they come._

* **Async Support**

_It's the 2020s – time to `await` all the things~!_

## Install

```
$ npm install --save polkadot
```

## Usage: CLI

Just specify an entry file – that's it! :tada:

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.

```sh
# Examples:

$ polkadot
$ polkadot app.js
$ PORT=8080 polkadot app.js
```

```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](#api) is available.

Similarly, this is the only way to customize the underlying `server` itself (see the [`with-https`](https://github.com/lukeed/polkadot/tree/master/examples/with-https) example).

```js
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](#usage-programmatic) only

### polkadot.handler(req, res)
Returns: `Function`

The main `polkadot` handler.

It parses the `req` (see [`IncomingMessage`](https://nodejs.org/api/http.html#http_class_http_incomingmessage)) and assigns value to the `path`, `query`, and `search` keys1.

It also waits until the `res` (see [`ServerResponse`](https://nodejs.org/api/http.html#http_class_http_serverresponse)) has been terminated or until data is returned.2

> _1 See [`@polka/url`](https://github.com/lukeed/polka/tree/master/packages/url) for further details._

> _2 See [Handlers](#handlers) for varying return types._

### polkadot.listen()
Returns: `http.Server`

Boots (or creates) the underlying [`http.Server`](https://nodejs.org/api/http.html#http_class_http_server) for the first time.

All arguments are passed directly to [`server.listen`](https://nodejs.org/api/net.html#net_server_listen) with no changes.

## Handlers

Every handler receives a `req` ([`IncomingMessage`](https://nodejs.org/api/http.html#http_class_http_incomingmessage)) and a `res` ([`ServerResponse`](https://nodejs.org/api/http.html#http_class_http_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`](https://github.com/lukeed/polka/tree/master/packages/url) assigns value to `req.path`, `req.query`, and `req.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`](https://nodejs.org/api/http.html#http_class_http_serverresponse)), you can set headers, the statusCode, and/or write response data with the core Node.js methods:

```js
module.exports = function (req, res) {
res.statusCode = 400;
res.setHeader('Content-Type', 'application/json');
res.end('{"error":"Bad Request"}');
}
```

### 2. The `@polka/send-type` library

The [`@polka/send-type`](https://github.com/lukeed/polka/tree/master/packages/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](https://github.com/lukeed/polka/tree/master/packages/send-type#data-detections) documentation.

Because the [`@polka/send-type`](https://github.com/lukeed/polka/tree/master/packages/send-type) library is already a dependency of `polkadot`, using it comes at _no extra cost_!

```js
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`](https://github.com/lukeed/polka/tree/master/packages/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](https://github.com/lukeed/polka/tree/master/packages/send-type#data-detections) documentation.

```js
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 versions `7.4` and above.

```js
// 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`](https://github.com/lukeed/polka/tree/master/packages/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`](https://github.com/lukeed/polkadot/tree/master/examples/with-router) example that uses [Trouter](https://github.com/lukeed/trouter) to build a full JSON resource.

Below is a simple example that serves images & video files based on the incoming path:

```js
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`](https://github.com/lukeed/polkadot/tree/master/examples/with-middleware) or the [`with-router`](https://github.com/lukeed/polkadot/tree/master/examples/with-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):

```js
// 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`](/bench) directory.

## Prior Art

Polkadot is the "little sibling" to [Polka](https://github.com/lukeed/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`](https://github.com/zeit/micro), which was the first HTTP framework of our kind (to my knowledge).

## License

MIT © [Luke Edwards](https://lukeed.com)