Introducing Polka: A micro web server.
Tags: node, javascript
Published: 2018-01-28 11:37:19

---
title: "Introducing Polka: A micro web server."
published: true
description: the introduction of a new nodejs web server.
tags: node, nodejs, JavaScript
---
![polka](https://thepracticaldev.s3.amazonaws.com/i/qz07w2nqfdarbkbpfs93.png)
                      
# What is Polka ?
Polka is an extremely minimal, highly performant Express.js alternative. 

## Installation.
```
npm i --save polka
```

## Usage
Polka has similar patterns with ExpressJS in terms of routing and API calls.

### Simple Example.
```js
const polka = require('polka');

polka()
  .get('/', (req, res) => {
	  res.end('Hello there !');
  })
  .listen(9000).then(_ => {
    console.log(`> Running on localhost:3000`);
  });
```

## Polka's API
Polka has four (4) main API methods.
* **Polka(options)** - Returns an instance of polka.
* **use(base, ..fn)**
* **parse(req)**
* **listen(port, hostname)**
* **handler(req, res, parsed)**

## Routing with Polka.
Routes are used to define how an application responds to varying HTTP methods and endpoints.
### Basics

Each route is comprised of a [path pattern](#patterns), a [HTTP method](#methods), and a [handler](#handlers) (aka, what you want to do).

In code, this looks like:

```js
app.METHOD(pattern, handler);
```

wherein:

* `app` is an instance of `polka`
*`method` is any valid HTTP method, lowercased
* `pattern` is a routing pattern (string)
* `handler` is the function to execute when `pattern` is matched

Also, a single pathname (or `pattern`) may be reused with multiple METHODs.

The following example demonstrates some simple routes.

```js
const app = polka();

app.get('/', (req, res) => {
  res.end('Hello world!');
});

app.get('/users', (req, res) => {
  res.end('Get all users!');
});

app.post('/users', (req, res) => {
  res.end('Create a new User!');
});

app.put('/users/:id', (req, res) => {
  res.end(`Update User with ID of ${req.params.id}`);
});

app.delete('/users/:id', (req, res) => {
  res.end(`CY@ User ${req.params.id}!`);
});
```
## Patterns

Unlike the very popular [`path-to-regexp`](https://github.com/pillarjs/path-to-regexp), Polka uses string comparison to locate route matches. While [faster](https://github.com/lukeed/matchit#benchmarks) & more memory efficient, this does also prevent complex pattern matching.

However, have no fear! :boom: All the basic and most commonly used patterns are supported. You probably only ever used these patterns in the first place. :wink:

> See [`comparison`](#comparions) for the list of `RegExp`-based patterns that Polka does not support.

The supported pattern types are:

* static (`/users`)
* named parameters (`/users/:id`)
* nested parameters (`/users/:id/books/:title`)
* optional parameters (`/users/:id?/books/:title?`)
* any match / wildcards (`/users/*`)

### Parameters

Any named parameters included within your route `pattern` will be automatically added to your incoming `req` object. All parameters will be found within `req.params` under the same name they were given.

> **Important:** Your parameter names should be unique, as shared names will overwrite each other!

```js
app.get('/users/:id/books/:title', (req, res) => {
  let { id, title } = req.params;
  res.end(`User: ${id} && Book: ${title}`);
});
```

```sh
$ curl /users/123/books/Narnia
#=> User: 123 && Book: Narnia
```

## Methods

Any valid HTTP method is supported! However, only the most common methods are used throughout this documentation for demo purposes.

> **Note:** For a full list of valid METHODs, please see [this list](http://expressjs.com/en/4x/api.html#routing-methods).

## Handlers

Request handlers accept the incoming [`ClientRequest`](https://nodejs.org/dist/latest-v9.x/docs/api/http.html#http_class_http_clientrequest) and the formulating [`ServerResponse`](https://nodejs.org/dist/latest-v9.x/docs/api/http.html#http_class_http_serverresponse).

Every route definition must contain a valid `handler` function, or else an error will be thrown at runtime.

> **Important:** You must _always_ terminate a `ServerResponse`!

It's a **very good** practice to _always_ terminate your response ([`res.end`](https://nodejs.org/api/http.html#http_request_end_data_encoding_callback)) inside a handler, even if you expect a middleware to do it for you. In the event a response is/was not terminated, the server will hang & eventually exit with a `TIMEOUT` error.

> **Note:** This is a native `http` behavior.

#### Async Handlers

If using Node 7.4 or later, you may leverage native `async` and `await` syntax! :heart_eyes_cat:

No special preparation is needed — simply add the appropriate keywords.

```js
const app = polka();

const sleep = ms => new Promise(r => setTimeout(r, ms));

async function authenticate(req, res, next) {
  let token = req.getHeader('authorization');
  if (!token) return app.send(res, 401);
  req.user = await Users.find(token); // <== fake
  next(); // done, woot!
}

app
  .use(authenticate)
  .get('/', async (req, res) => {
    // log middleware's findings
    console.log('~> current user', req.user);
    // force sleep, because we can~!
    await sleep(500);
    // send greeting
    res.end(`Hello, ${req.user.name}`);
  });
```


## Middleware

Middleware are functions that run in between (hence "middle") receiving the request & executing your route's `handler` response.

> Coming from Express? Use any middleware you already know & love! :tada:

The middleware signature receives the request (`req`), the response (`res`), and a callback (`next`).

These can apply mutations to the `req` and `res` objects, and unlike Express, have access to `req.params`, `req.pathname`, `req.search`, and `req.query`!

Most importantly, a middleware ***must*** either call `next()` or terminate the response (`res.end`). Failure to do this will result in a never-ending response, which will eventually crash the `http.Server`.

```js
// Log every request
function logger(req, res, next) {
  console.log(`~> Received ${req.method} on ${req.url}`);
  next(); // move on
}

function authorize(req, res, next) {
  // mutate req; available later
  req.token = req.getHeader('authorization');
  req.token ? next() : ((res.statusCode=401) && res.end('No token!'));
}

polka().use(logger, authorize).get('*', (req, res) => {
  console.log(`~> user token: ${req.token}`);
  res.end('Hello, valid user');
});
```


```sh
$ curl /
# ~> Received GET on /
#=> (401) No token!

$ curl -H "authorization: secret" /foobar
# ~> Received GET on /foobar
# ~> user token: secret
#=> (200) Hello, valid user
```

In Polka, middleware functions are mounted globally, which means that they'll run on every request. Instead, you'll have to apply internal filters to determine when & where your middleware should run.

> **Note:** This might change in Polka 1.0 :thinking:

```js
function foobar(req, res, next) {
  if (req.pathname.startsWith('/users')) {
    // do something magical
  }
  next();
}
```

### Middleware Errors

If an error arises within a middleware, the loop will be exited. This means that no other middleware will execute & neither will the route handler.

Similarly, regardless of `statusCode`, an early response termination will also exit the loop & prevent the route handler from running.

There are three ways to "throw" an error from within a middleware function.

> **Hint:** None of them use `throw`

1. **Pass any string to `next()` **

    This will exit the loop & send a `500` status code, with your error string as the response body.

```js
    polka()
      .use((req, res, next) => next('💩'))
      .get('*', (req, res) => res.end('wont run'));
	 
```
   
```bash
    $ curl /
    #=> (500) 
```
   

2. **Pass an `Error` to `next()`**

    This is similar to the above option, but gives you a window in changing the `statusCode` to something other than the `500` default.

```js    
function oopsies(req, res, next) {
  let err = new Error('Try again');
  err.code = 422;
  next(err);
}
```

```bash
    $ curl /
    #=> (422) Try again
```

3. **Terminate the response early**

    Once the response has been ended, there's no reason to continue the loop!

    This approach is the most versatile as it allows to control every aspect of the outgoing `res` .

```js
    function oopsies(req, res, next) {
      if (true) {
        // something bad happened~
        res.writeHead(400, {
          'Content-Type': 'application/json',
          'X-Error-Code': 'Please dont do this IRL'
        });
        let json = JSON.stringify({ error:'Missing CSRF token' });
        res.end(json);
      } else {
        next(); // never called FYI
      }
    }
```
    

```bash
$ curl /
    #=> (400) {"error":"Missing CSRF token"}
```


## Benchmarks

A round of Polka-vs-Express benchmarks across varying Node versions can be [found here](https://github.com/lukeed/polka/tree/master/bench).

> **Important:** Time is mostly spent in _your application code_ rather than Express or Polka code! Switching from Express to Polka will (likely) not show such drastic performance gains.

```
Node 8.9.0

Native
    Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency     2.24ms  112.34us   5.57ms   92.15%
        Req/Sec     5.38k    99.48     5.57k    81.81%
      432562 requests in 10.10s, 42.90MB read
    Requests/sec:  42815.14
    Transfer/sec:      4.25MB

Polka
    Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency     2.26ms  115.55us   5.19ms   87.16%
        Req/Sec     5.32k    97.34     5.55k    72.77%
      428208 requests in 10.10s, 42.47MB read
    Requests/sec:  42388.92
    Transfer/sec:      4.20MB

Express
    Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency     5.15ms  421.69us   8.51ms   77.95%
        Req/Sec     2.34k    77.06     2.55k    72.12%
      186390 requests in 10.01s, 36.97MB read
    Requests/sec:  18628.36
    Transfer/sec:      3.70MB

Fastify
    Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency     2.91ms  201.13us   7.51ms   58.07%
        Req/Sec     4.14k   130.04     4.48k    65.59%
      333158 requests in 10.10s, 41.30MB read
    Requests/sec:  32979.84
    Transfer/sec:      4.09MB

Koa
    Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency     3.43ms  369.96us   8.67ms   87.30%
        Req/Sec     3.51k   114.78     4.12k    69.76%
      281808 requests in 10.10s, 38.97MB read
    Requests/sec:  27892.99
    Transfer/sec:      3.86MB
```


## Comparisons

Polka's API aims to be _very_ similar to Express since most Node.js developers are already familiar with it. If you know Express, you already know Polka! :dancer:

There are, however, a few main differences. Polka does not support or offer:

1) **Any built-in view/rendering engines.**
Most templating engines can be incorporated into middleware functions or used directly within a route handler.

2) **The ability to `throw` from within middleware.**
However, all other forms of middleware-errors are supported.( see middleware options)


```js
	function middleware(res, res, next) {
  // pass an error message to next()
  next('uh oh');

  // pass an Error to next()
  next(new Error('🙀'));

  // send an early, customized error response
  res.statusCode = 401;
  res.end('Who are you?');
}
```
   

3) **Express-like response helpers... yet! (#14)**
Express has a nice set of [response helpers](http://expressjs.com/en/4x/api.html#res.append). While Polka relies on the [native Node.js response methods](https://nodejs.org/dist/latest-v9.x/docs/api/http.html#http_class_http_serverresponse), it would be very easy/possible to attach a global middleware that contained a similar set of helpers. (_TODO_)

4) **`RegExp`-based route patterns.**
Polka's router uses string comparison to match paths against patterns. It's a lot quicker & more efficient.
The following routing patterns **are not** supported:
	
```js

app.get('/ab?cd', _ => {});
app.get('/ab+cd', _ => {});
app.get('/ab*cd', _ => {});
app.get('/ab(cd)?e', _ => {});
app.get(/a/, _ => {});
app.get(/.*fly$/, _ => {});

```
The following routing patterns **are** supported:

```js
    app.get('/users', _ => {});
    app.get('/users/:id', _ => {});
    app.get('/users/:id?', _ => {});
    app.get('/users/:id/books/:title', _ => {});
    app.get('/users/*', _ => {});
```
	
## Credits.
All credits goes to [Luke Edwards](http://twitter.com/lukeed05) for his awesome works.