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