1 | <div align="center">
|
2 | <img src="https://github.com/lukeed/polka/raw/master/polka.png" alt="Polka" width="400" height="300" />
|
3 | </div>
|
4 |
|
5 | <h1 align="center">Polka</h1>
|
6 |
|
7 | <div align="center">
|
8 | <a href="https://npmjs.org/package/polka">
|
9 | <img src="https://badgen.now.sh/npm/v/polka" alt="version" />
|
10 | </a>
|
11 | <a href="https://travis-ci.org/lukeed/polka">
|
12 | <img src="https://badgen.now.sh/travis/lukeed/polka" alt="travis" />
|
13 | </a>
|
14 | <a href="https://codecov.io/gh/lukeed/polka">
|
15 | <img src="https://badgen.now.sh/codecov/c/github/lukeed/polka" alt="codecov" />
|
16 | </a>
|
17 | <a href="https://npmjs.org/package/polka">
|
18 | <img src="https://badgen.now.sh/npm/dm/polka" alt="downloads" />
|
19 | </a>
|
20 | <a href="https://packagephobia.now.sh/result?p=polka">
|
21 | <img src="https://packagephobia.now.sh/badge?p=polka" alt="install size" />
|
22 | </a>
|
23 | </div>
|
24 |
|
25 | <div align="center">A micro web server so fast, it'll make you dance! :dancers:</div>
|
26 |
|
27 | <br />
|
28 |
|
29 | Polka is an extremely minimal, highly performant Express.js alternative. Yes, you're right, Express is _already_ super fast & not _that_ big :thinking: — but Polka shows that there was (somehow) room for improvement!
|
30 |
|
31 | Essentially, Polka is just a [native HTTP server](https://nodejs.org/dist/latest-v9.x/docs/api/http.html#http_class_http_server) with added support for routing, middleware, and sub-applications. That's it! :tada:
|
32 |
|
33 | And, of course, in mandatory bullet-point format:
|
34 |
|
35 | * 33-50% faster than Express for simple applications
|
36 | * Middleware support, including Express middleware you already know & love
|
37 | * Nearly identical application API & route pattern definitions
|
38 | * ~90 LOC for Polka, 120 including [its router](https://github.com/lukeed/trouter)
|
39 |
|
40 |
|
41 | ## Install
|
42 |
|
43 | ```
|
44 | $ npm install --save polka
|
45 | ```
|
46 |
|
47 | ## Usage
|
48 |
|
49 | ```js
|
50 | const polka = require('polka');
|
51 |
|
52 | function one(req, res, next) {
|
53 | req.hello = 'world';
|
54 | next();
|
55 | }
|
56 |
|
57 | function two(req, res, next) {
|
58 | req.foo = '...needs better demo 😔';
|
59 | next();
|
60 | }
|
61 |
|
62 | polka()
|
63 | .use(one, two)
|
64 | .get('/users/:id', (req, res) => {
|
65 | console.log(`~> Hello, ${req.hello}`);
|
66 | res.end(`User: ${req.params.id}`);
|
67 | })
|
68 | .listen(3000, err => {
|
69 | if (err) throw err;
|
70 | console.log(`> Running on localhost:3000`);
|
71 | });
|
72 | ```
|
73 |
|
74 | ## API
|
75 |
|
76 | Polka extends [Trouter](https://github.com/lukeed/trouter) which means it inherits its API, too!
|
77 |
|
78 | ### polka(options)
|
79 |
|
80 | Returns an instance of Polka~!
|
81 |
|
82 | #### options.server
|
83 | Type: `Server`<br>
|
84 |
|
85 | A custom, instantiated server that the Polka instance should attach its [`handler`](#handlerreq-res-parsed) to. This is useful if you have initialized a server elsewhere in your application and want Polka to use _it_ instead of creating a new `http.Server`.
|
86 |
|
87 | Polka _only_ updates the server when [`polka.listen`](#listen) is called. At this time, Polka will create a [`http.Server`](https://nodejs.org/api/http.html#http_class_http_server) if a server was not already provided via `options.server`.
|
88 |
|
89 | > **Important:** The `server` key will be `undefined` until `polka.listen` is invoked, unless a server was provided.
|
90 |
|
91 | #### options.onError
|
92 | Type: `Function`
|
93 |
|
94 | A catch-all error handler; executed whenever a middleware throws an error. Change this if you don't like default behavior.
|
95 |
|
96 | Its signature is `(err, req, res, next)`, where `err` is the `String` or `Error` thrown by your middleware.
|
97 |
|
98 | > **Caution:** Use `next()` to bypass certain errors **at your own risk!** <br>You must be certain that the exception will be handled elsewhere or _can_ be safely ignored. <br>Otherwise your response will never terminate!
|
99 |
|
100 | #### options.onNoMatch
|
101 | Type: `Function`
|
102 |
|
103 | A handler when no route definitions were matched. Change this if you don't like default behavior, which sends a `404` status & `Not found` response.
|
104 |
|
105 | Its signature is `(req, res)` and requires that you terminate the response.
|
106 |
|
107 |
|
108 | ### use(base, ...fn)
|
109 |
|
110 | Attach [middleware(s)](#middleware) and/or sub-application(s) to the server. These will execute _before_ your routes' [handlers](#handlers).
|
111 |
|
112 | **Important:** If a `base` pathname is provided, all functions within the same `use()` block will _only_ execute when the `req.path` matches the `base` path.
|
113 |
|
114 | #### base
|
115 | Type: `String`<br>
|
116 | Default: `undefined`
|
117 |
|
118 | The base path on which the following middleware(s) or sub-application should be mounted.
|
119 |
|
120 | #### fn
|
121 | Type: `Function|Array`
|
122 |
|
123 | You may pass one or more functions at a time. Each function must have the standardized `(req, res, next)` signature.
|
124 |
|
125 | You may also pass a sub-application, which _must_ be accompanied by a `base` pathname.
|
126 |
|
127 | Please see [`Middleware`](#middleware) and [Express' middleware examples](http://expressjs.com/en/4x/api.html#middleware-callback-function-examples) for more info.
|
128 |
|
129 |
|
130 | ### parse(req)
|
131 |
|
132 | Returns: `Object` or `undefined`
|
133 |
|
134 | As of `v0.5.0`, this is an alias of the [`@polka/url`](/packages/url) module. For nearly all cases, you'll notice no changes.
|
135 |
|
136 | But, for whatever reason, you can quickly swap in [`parseurl`](https://github.com/pillarjs/parseurl) again:
|
137 |
|
138 | ```js
|
139 | const app = polka();
|
140 | app.parse = require('parseurl');
|
141 | //=> Done!
|
142 | ```
|
143 |
|
144 | ### listen()
|
145 |
|
146 | Returns: `Polka`
|
147 |
|
148 | Boots (or creates) the underlying [`http.Server`](https://nodejs.org/dist/latest-v9.x/docs/api/http.html#http_class_http_server) for the first time. All arguments are passed to [`server.listen`](https://nodejs.org/dist/latest-v9.x/docs/api/net.html#net_server_listen) directly with no changes.
|
149 |
|
150 | As of `v0.5.0`, this method no longer returns a Promise. Instead, the current Polka instance is returned directly, allowing for chained operations.
|
151 |
|
152 | ```js
|
153 | // Could not do this before 0.5.0
|
154 | const { server, handler } = polka().listen();
|
155 |
|
156 | // Or this!
|
157 | const app = polka().listen(PORT, onAppStart);
|
158 |
|
159 | app.use('users', require('./users'))
|
160 | .get('/', (req, res) => {
|
161 | res.end('Pretty cool!');
|
162 | });
|
163 | ```
|
164 |
|
165 | ### handler(req, res, parsed)
|
166 |
|
167 | The main Polka [`IncomingMessage`](https://nodejs.org/api/http.html#http_class_http_incomingmessage) handler. It receives all requests and tries to match the incoming URL against known routes.
|
168 |
|
169 | If the `req.url` is not immediately matched, Polka will look for sub-applications or middleware groups matching the `req.url`'s [`base`](#base) path. If any are found, they are appended to the loop, running _after_ any global middleware.
|
170 |
|
171 | > **Note:** Any middleware defined within a sub-application is run _after_ the main app's (aka, global) middleware and _before_ the sub-application's route handler.
|
172 |
|
173 | At the end of the loop, if a middleware hasn't yet terminated the response (or thrown an error), the route handler will be executed, if found — otherwise a `(404) Not found` response is returned, configurable via [`options.onNoMatch`](#optionsonnomatch).
|
174 |
|
175 | #### req
|
176 | Type: `IncomingMessage`
|
177 |
|
178 | #### res
|
179 | Type: `ServerResponse`
|
180 |
|
181 | #### parsed
|
182 | Type: `Object`
|
183 |
|
184 | Optionally provide a parsed [URL](https://nodejs.org/dist/latest-v9.x/docs/api/url.html#url_class_url) object. Useful if you've already parsed the incoming path. Otherwise, [`app.parse`](#parsereq) (aka [`parseurl`](https://github.com/pillarjs/parseurl)) will run by default.
|
185 |
|
186 |
|
187 | ## Routing
|
188 |
|
189 | Routes are used to define how an application responds to varying HTTP methods and endpoints.
|
190 |
|
191 | > If you're coming from Express, there's nothing new here!<br> However, do check out [Comparisons](#comparisons) for some pattern changes.
|
192 |
|
193 | ### Basics
|
194 |
|
195 | Each route is comprised of a [path pattern](#patterns), a [HTTP method](#methods), and a [handler](#handlers) (aka, what you want to do).
|
196 |
|
197 | In code, this looks like:
|
198 |
|
199 | ```js
|
200 | app.METHOD(pattern, handler);
|
201 | ```
|
202 |
|
203 | wherein:
|
204 |
|
205 | * `app` is an instance of `polka`
|
206 | * [`METHOD`](#methods) is any valid HTTP/1.1 method, lowercased
|
207 | * [`pattern`](#patterns) is a routing pattern (string)
|
208 | * [`handler`](#handlers) is the function to execute when `pattern` is matched
|
209 |
|
210 | Also, a single pathname (or `pattern`) may be reused with multiple METHODs.
|
211 |
|
212 | The following example demonstrates some simple routes.
|
213 |
|
214 | ```js
|
215 | const app = polka();
|
216 |
|
217 | app.get('/', (req, res) => {
|
218 | res.end('Hello world!');
|
219 | });
|
220 |
|
221 | app.get('/users', (req, res) => {
|
222 | res.end('Get all users!');
|
223 | });
|
224 |
|
225 | app.post('/users', (req, res) => {
|
226 | res.end('Create a new User!');
|
227 | });
|
228 |
|
229 | app.put('/users/:id', (req, res) => {
|
230 | res.end(`Update User with ID of ${req.params.id}`);
|
231 | });
|
232 |
|
233 | app.delete('/users/:id', (req, res) => {
|
234 | res.end(`CY@ User ${req.params.id}!`);
|
235 | });
|
236 | ```
|
237 |
|
238 | ### Patterns
|
239 |
|
240 | 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.
|
241 |
|
242 | 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:
|
243 |
|
244 | > See [Comparisons](#comparisons) for the list of `RegExp`-based patterns that Polka does not support.
|
245 |
|
246 | The supported pattern types are:
|
247 |
|
248 | * static (`/users`)
|
249 | * named parameters (`/users/:id`)
|
250 | * nested parameters (`/users/:id/books/:title`)
|
251 | * optional parameters (`/users/:id?/books/:title?`)
|
252 | * any match / wildcards (`/users/*`)
|
253 |
|
254 | ### Parameters
|
255 |
|
256 | Any named parameters included within your route [`pattern`](#patterns) will be automatically added to your incoming `req` object. All parameters will be found within `req.params` under the same name they were given.
|
257 |
|
258 | > **Important:** Your parameter names should be unique, as shared names will overwrite each other!
|
259 |
|
260 | ```js
|
261 | app.get('/users/:id/books/:title', (req, res) => {
|
262 | let { id, title } = req.params;
|
263 | res.end(`User: ${id} && Book: ${title}`);
|
264 | });
|
265 | ```
|
266 |
|
267 | ```sh
|
268 | $ curl /users/123/books/Narnia
|
269 | #=> User: 123 && Book: Narnia
|
270 | ```
|
271 |
|
272 | ### Methods
|
273 |
|
274 | Any valid HTTP/1.1 method is supported! However, only the most common methods are used throughout this documentation for demo purposes.
|
275 |
|
276 | > **Note:** For a full list of valid METHODs, please see [this list](https://github.com/lukeed/trouter#method).
|
277 |
|
278 | ### Handlers
|
279 |
|
280 | Request handlers accept the incoming [`IncomingMessage`](https://nodejs.org/api/http.html#http_class_http_incomingmessage) and the formulating [`ServerResponse`](https://nodejs.org/dist/latest-v9.x/docs/api/http.html#http_class_http_serverresponse).
|
281 |
|
282 | Every route definition must contain a valid `handler` function, or else an error will be thrown at runtime.
|
283 |
|
284 | > **Important:** You must _always_ terminate a `ServerResponse`!
|
285 |
|
286 | 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](#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.
|
287 |
|
288 | > **Note:** This is a native `http` behavior.
|
289 |
|
290 | #### Async Handlers
|
291 |
|
292 | If using Node 7.4 or later, you may leverage native `async` and `await` syntax! :heart_eyes_cat:
|
293 |
|
294 | No special preparation is needed — simply add the appropriate keywords.
|
295 |
|
296 | ```js
|
297 | const app = polka();
|
298 |
|
299 | const sleep = ms => new Promise(r => setTimeout(r, ms));
|
300 |
|
301 | async function authenticate(req, res, next) {
|
302 | let token = req.headers['authorization'];
|
303 | if (!token) return (res.statusCode=401,res.end('No token!'));
|
304 | req.user = await Users.find(token); // <== fake
|
305 | next(); // done, woot!
|
306 | }
|
307 |
|
308 | app
|
309 | .use(authenticate)
|
310 | .get('/', async (req, res) => {
|
311 | // log middleware's findings
|
312 | console.log('~> current user', req.user);
|
313 | // force sleep, because we can~!
|
314 | await sleep(500);
|
315 | // send greeting
|
316 | res.end(`Hello, ${req.user.name}`);
|
317 | });
|
318 | ```
|
319 |
|
320 |
|
321 | ## Middleware
|
322 |
|
323 | Middleware are functions that run in between (hence "middle") receiving the request & executing your route's [`handler`](#handlers) response.
|
324 |
|
325 | > Coming from Express? Use any middleware you already know & love! :tada:
|
326 |
|
327 | The middleware signature receives the request (`req`), the response (`res`), and a callback (`next`).
|
328 |
|
329 | These can apply mutations to the `req` and `res` objects, and unlike Express, have access to `req.params`, `req.path`, `req.search`, and `req.query`!
|
330 |
|
331 | 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`.
|
332 |
|
333 | ```js
|
334 | // Log every request
|
335 | function logger(req, res, next) {
|
336 | console.log(`~> Received ${req.method} on ${req.url}`);
|
337 | next(); // move on
|
338 | }
|
339 |
|
340 | function authorize(req, res, next) {
|
341 | // mutate req; available later
|
342 | req.token = req.headers['authorization'];
|
343 | req.token ? next() : ((res.statusCode=401) && res.end('No token!'));
|
344 | }
|
345 |
|
346 | polka().use(logger, authorize).get('*', (req, res) => {
|
347 | console.log(`~> user token: ${req.token}`);
|
348 | res.end('Hello, valid user');
|
349 | });
|
350 | ```
|
351 |
|
352 | ```sh
|
353 | $ curl /
|
354 | # ~> Received GET on /
|
355 | #=> (401) No token!
|
356 |
|
357 | $ curl -H "authorization: secret" /foobar
|
358 | # ~> Received GET on /foobar
|
359 | # ~> user token: secret
|
360 | #=> (200) Hello, valid user
|
361 | ```
|
362 |
|
363 | ### Middleware Sequence
|
364 |
|
365 | In Polka, middleware functions are organized into tiers.
|
366 |
|
367 | Unlike Express, Polka middleware are tiered into "global", "filtered", and "route-specific" groups.
|
368 |
|
369 | * Global middleware are defined via `.use('/', ...fn)` or `.use(...fn)`, which are synonymous.<br>_Because_ every request's `pathname` begins with a `/`, this tier is always triggered.
|
370 |
|
371 | * Sub-group or "filtered" middleware are defined with a base `pathname` that's more specific than `'/'`. For example, defining `.use('/users', ...fn)` will run on any `/users/**/*` request.<br>These functions will execute _after_ "global" middleware but before the route-specific handler.
|
372 |
|
373 | * Route handlers match specific paths and execute last in the chain. They must also match the `method` action.
|
374 |
|
375 | Once the chain of middleware handler(s) has been composed, Polka will iterate through them sequentially until all functions have run, until a chain member has terminated the response early, or until a chain member has thrown an error.
|
376 |
|
377 | Contrast this with Express, which does not tier your middleware and instead iterates through your entire application in the sequence that you composed it.
|
378 |
|
379 | ```js
|
380 | // Express
|
381 | express()
|
382 | .get('/', get)
|
383 | .use(foo)
|
384 | .get('/users/123', user)
|
385 | .use('/users', users)
|
386 |
|
387 | // Polka
|
388 | Polka()
|
389 | .get('/', get)
|
390 | .use(foo)
|
391 | .get('/users/123', user)
|
392 | .use('/users', users)
|
393 | ```
|
394 |
|
395 | ```sh
|
396 | $ curl {APP}/
|
397 | # Express :: [get]
|
398 | # Polka :: [foo, get]
|
399 |
|
400 | $ curl {APP}/users/123
|
401 | # Express :: [foo, user]
|
402 | # Polka :: [foo, users, user]
|
403 | ```
|
404 |
|
405 |
|
406 | ### Middleware Errors
|
407 |
|
408 | 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.
|
409 |
|
410 | Similarly, regardless of `statusCode`, an early response termination will also exit the loop & prevent the route handler from running.
|
411 |
|
412 | There are three ways to "throw" an error from within a middleware function.
|
413 |
|
414 | > **Hint:** None of them use `throw` :joy_cat:
|
415 |
|
416 | 1. **Pass any string to `next()`**
|
417 |
|
418 | This will exit the loop & send a `500` status code, with your error string as the response body.
|
419 |
|
420 | ```js
|
421 | polka()
|
422 | .use((req, res, next) => next('💩'))
|
423 | .get('*', (req, res) => res.end('wont run'));
|
424 | ```
|
425 |
|
426 | ```sh
|
427 | $ curl /
|
428 | #=> (500) 💩
|
429 | ```
|
430 |
|
431 | 2. **Pass an `Error` to `next()`**
|
432 |
|
433 | This is similar to the above option, but gives you a window in changing the `statusCode` to something other than the `500` default.
|
434 |
|
435 | ```js
|
436 | function oopsies(req, res, next) {
|
437 | let err = new Error('Try again');
|
438 | err.code = 422;
|
439 | next(err);
|
440 | }
|
441 | ```
|
442 |
|
443 | ```sh
|
444 | $ curl /
|
445 | #=> (422) Try again
|
446 | ```
|
447 |
|
448 | 3. **Terminate the response early**
|
449 |
|
450 | Once the response has been ended, there's no reason to continue the loop!
|
451 |
|
452 | This approach is the most versatile as it allows to control every aspect of the outgoing `res`.
|
453 |
|
454 | ```js
|
455 | function oopsies(req, res, next) {
|
456 | if (true) {
|
457 | // something bad happened~
|
458 | res.writeHead(400, {
|
459 | 'Content-Type': 'application/json',
|
460 | 'X-Error-Code': 'Please dont do this IRL'
|
461 | });
|
462 | let json = JSON.stringify({ error:'Missing CSRF token' });
|
463 | res.end(json);
|
464 | } else {
|
465 | next(); // never called FYI
|
466 | }
|
467 | }
|
468 | ```
|
469 |
|
470 | ```sh
|
471 | $ curl /
|
472 | #=> (400) {"error":"Missing CSRF token"}
|
473 | ```
|
474 |
|
475 |
|
476 | ## Benchmarks
|
477 |
|
478 | Quick comparison between various frameworks using [`wrk`](https://github.com/wg/wrk) on `Node v10.4.0`.<br> Results are taken with the following command, after one warm-up run:
|
479 |
|
480 | ```
|
481 | $ wrk -t4 -c4 -d10s http://localhost:3000/users/123
|
482 | ```
|
483 |
|
484 | Additional benchmarks between Polka and Express (using various Node versions) can be [found here](/bench).
|
485 |
|
486 | > **Important:** Time is mostly spent in _your application code_ rather than Express or Polka code!<br> Switching from Express to Polka will (likely) not show such drastic performance gains.
|
487 |
|
488 | ```
|
489 | Native
|
490 | Thread Stats Avg Stdev Max +/- Stdev
|
491 | Latency 1.91ms 110.95us 5.54ms 93.08%
|
492 | Req/Sec 13.11k 308.16 13.46k 94.31%
|
493 | 526992 requests in 10.10s, 52.27MB read
|
494 | Requests/sec: 52177.09
|
495 | Transfer/sec: 5.18MB
|
496 |
|
497 | Polka
|
498 | Thread Stats Avg Stdev Max +/- Stdev
|
499 | Latency 1.97ms 103.56us 4.63ms 92.30%
|
500 | Req/Sec 12.76k 172.50 13.13k 85.75%
|
501 | 507836 requests in 10.00s, 50.37MB read
|
502 | Requests/sec: 50779.77
|
503 | Transfer/sec: 5.04MB
|
504 |
|
505 | Rayo
|
506 | Thread Stats Avg Stdev Max +/- Stdev
|
507 | Latency 2.02ms 116.55us 6.66ms 92.55%
|
508 | Req/Sec 12.43k 262.32 12.81k 91.58%
|
509 | 499795 requests in 10.10s, 49.57MB read
|
510 | Requests/sec: 49481.55
|
511 | Transfer/sec: 4.91MB
|
512 |
|
513 | Fastify
|
514 | Thread Stats Avg Stdev Max +/- Stdev
|
515 | Latency 2.10ms 138.04us 5.46ms 91.50%
|
516 | Req/Sec 11.96k 414.14 15.82k 95.04%
|
517 | 479518 requests in 10.10s, 66.31MB read
|
518 | Requests/sec: 47476.75
|
519 | Transfer/sec: 6.57MB
|
520 |
|
521 | Koa
|
522 | Thread Stats Avg Stdev Max +/- Stdev
|
523 | Latency 2.95ms 247.10us 6.91ms 72.18%
|
524 | Req/Sec 8.52k 277.12 9.09k 70.30%
|
525 | 342518 requests in 10.10s, 47.36MB read
|
526 | Requests/sec: 33909.82
|
527 | Transfer/sec: 4.69MB
|
528 |
|
529 | Express
|
530 | Thread Stats Avg Stdev Max +/- Stdev
|
531 | Latency 4.91ms 484.52us 10.65ms 89.71%
|
532 | Req/Sec 5.11k 350.75 9.69k 98.51%
|
533 | 204520 requests in 10.10s, 40.57MB read
|
534 | Requests/sec: 20249.80
|
535 | Transfer/sec: 4.02MB
|
536 | ```
|
537 |
|
538 |
|
539 | ## Comparisons
|
540 |
|
541 | 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:
|
542 |
|
543 | There are, however, a few main differences. Polka does not support or offer:
|
544 |
|
545 | 1) **Polka uses a tiered middleware system.**
|
546 |
|
547 | Express maintains the sequence of your route & middleware declarations during its runtime, which can pose a problem when composing sub-applications. Typically, this forces you to duplicate groups of logic.
|
548 |
|
549 | Please see [Middleware Sequence](#middleware-sequence) for an example and additional details.
|
550 |
|
551 | 2) **Any built-in view/rendering engines.**
|
552 |
|
553 | Most templating engines can be incorporated into middleware functions or used directly within a route handler.
|
554 |
|
555 | 3) **The ability to `throw` from within middleware.**
|
556 |
|
557 | However, all other forms of middleware-errors are supported. (See [Middleware Errors](#middleware-errors).)
|
558 |
|
559 | ```js
|
560 | function middleware(res, res, next) {
|
561 | // pass an error message to next()
|
562 | next('uh oh');
|
563 |
|
564 | // pass an Error to next()
|
565 | next(new Error('🙀'));
|
566 |
|
567 | // send an early, customized error response
|
568 | res.statusCode = 401;
|
569 | res.end('Who are you?');
|
570 | }
|
571 | ```
|
572 |
|
573 | 4) **Express-like response helpers... yet! (#14)**
|
574 |
|
575 | 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_)
|
576 |
|
577 | 5) **`RegExp`-based route patterns.**
|
578 |
|
579 | Polka's router uses string comparison to match paths against patterns. It's a lot quicker & more efficient.
|
580 |
|
581 | The following routing patterns **are not** supported:
|
582 |
|
583 | ```js
|
584 | app.get('/ab?cd', _ => {});
|
585 | app.get('/ab+cd', _ => {});
|
586 | app.get('/ab*cd', _ => {});
|
587 | app.get('/ab(cd)?e', _ => {});
|
588 | app.get(/a/, _ => {});
|
589 | app.get(/.*fly$/, _ => {});
|
590 | ```
|
591 |
|
592 | The following routing patterns **are** supported:
|
593 |
|
594 | ```js
|
595 | app.get('/users', _ => {});
|
596 | app.get('/users/:id', _ => {});
|
597 | app.get('/users/:id?', _ => {});
|
598 | app.get('/users/:id/books/:title', _ => {});
|
599 | app.get('/users/*', _ => {});
|
600 | ```
|
601 |
|
602 | 6) **Polka instances are not (directly) the request handler.**
|
603 |
|
604 | Most packages in the Express ecosystem expect you to pass your `app` directly into the package. This is because `express()` returns a middleware signature directly.
|
605 |
|
606 | In the Polka-sphere, this functionality lives in your application's [`handler`](#handlerreq-res-parsed) instead.
|
607 |
|
608 | Here's an example with [`supertest`](https://github.com/visionmedia/supertest), a popular testing utility for Express apps.
|
609 |
|
610 | ```js
|
611 | const request = require('supertest');
|
612 | const send = require('@polka/send-type');
|
613 |
|
614 | const express = require('express')();
|
615 | const polka = require('polka')();
|
616 |
|
617 | express.get('/user', (req, res) => {
|
618 | res.status(200).json({ name: 'john' });
|
619 | });
|
620 |
|
621 | polka.get('/user', (req, res) => {
|
622 | send(res, 200, { name: 'john' });
|
623 | });
|
624 |
|
625 | function isExpected(app) {
|
626 | request(app)
|
627 | .get('/user')
|
628 | .expect('Content-Type', /json/)
|
629 | .expect('Content-Length', '15')
|
630 | .expect(200);
|
631 | }
|
632 |
|
633 | // Express: Pass in the entire application directly
|
634 | isExpected(express);
|
635 |
|
636 | // Polka: Pass in the application `handler` instead
|
637 | isExpected(polka.handler);
|
638 | ```
|
639 |
|
640 |
|
641 | ## License
|
642 |
|
643 | MIT © [Luke Edwards](https://lukeed.com)
|