{"id":13394341,"url":"https://github.com/lukeed/polka","last_synced_at":"2025-09-09T20:46:55.345Z","repository":{"id":41281023,"uuid":"115081766","full_name":"lukeed/polka","owner":"lukeed","description":"A micro web server so fast, it'll make you dance! :dancers:","archived":false,"fork":false,"pushed_at":"2025-04-09T18:42:18.000Z","size":772,"stargazers_count":5511,"open_issues_count":29,"forks_count":173,"subscribers_count":69,"default_branch":"main","last_synced_at":"2025-09-06T11:59:34.440Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lukeed.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"license","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"lukeed","open_collective":"polka"}},"created_at":"2017-12-22T05:55:41.000Z","updated_at":"2025-09-06T05:57:09.000Z","dependencies_parsed_at":"2023-09-29T08:53:18.528Z","dependency_job_id":"ff37cbcc-a723-4b3b-8bca-68e374d4bde2","html_url":"https://github.com/lukeed/polka","commit_stats":{"total_commits":207,"total_committers":13,"mean_commits":"15.923076923076923","dds":0.07729468599033817,"last_synced_commit":"a72e13cf3f674760ffa15ad3700aa1251f77795e"},"previous_names":[],"tags_count":44,"template":false,"template_full_name":null,"purl":"pkg:github/lukeed/polka","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lukeed%2Fpolka","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lukeed%2Fpolka/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lukeed%2Fpolka/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lukeed%2Fpolka/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lukeed","download_url":"https://codeload.github.com/lukeed/polka/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lukeed%2Fpolka/sbom","scorecard":{"id":604506,"data":{"date":"2025-08-11","repo":{"name":"github.com/lukeed/polka","commit":"c36cf8b5d1a57164e64fdd7d4e0d0f80ab262dc0"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.7,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":3,"reason":"Found 9/27 approved changesets -- score normalized to 3","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/ci.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/lukeed/polka/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/lukeed/polka/ci.yml/main?enable=pin","Warn: npmCommand not pinned by hash: .github/workflows/ci.yml:23","Warn: npmCommand not pinned by hash: .github/workflows/ci.yml:24","Warn: downloadThenRun not pinned by hash: .github/workflows/ci.yml:33","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   2 npmCommand dependencies pinned","Info:   0 out of   1 downloadThenRun dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: license:0","Info: FSF or OSI recognized license: MIT License: license:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 12 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-21T01:16:04.019Z","repository_id":41281023,"created_at":"2025-08-21T01:16:04.019Z","updated_at":"2025-08-21T01:16:04.019Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273919159,"owners_count":25191204,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-06T02:00:13.247Z","response_time":2576,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-30T17:01:16.487Z","updated_at":"2025-09-09T20:46:55.276Z","avatar_url":"https://github.com/lukeed.png","language":"JavaScript","funding_links":["https://github.com/sponsors/lukeed","https://opencollective.com/polka"],"categories":["JavaScript","Packages","📦 Legacy \u0026 Inactive Projects"],"sub_categories":["Web frameworks","HTTP"],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"polka.png\" alt=\"Polka\" width=\"400\" /\u003e\n\u003c/div\u003e\n\n\u003ch1 align=\"center\"\u003ePolka\u003c/h1\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003ca href=\"https://npmjs.org/package/polka\"\u003e\n    \u003cimg src=\"https://badgen.now.sh/npm/v/polka\" alt=\"version\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://travis-ci.org/lukeed/polka\"\u003e\n    \u003cimg src=\"https://badgen.now.sh/travis/lukeed/polka\" alt=\"travis\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://codecov.io/gh/lukeed/polka\"\u003e\n    \u003cimg src=\"https://badgen.now.sh/codecov/c/github/lukeed/polka\" alt=\"codecov\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://npmjs.org/package/polka\"\u003e\n    \u003cimg src=\"https://badgen.now.sh/npm/dm/polka\" alt=\"downloads\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://packagephobia.now.sh/result?p=polka\"\u003e\n    \u003cimg src=\"https://packagephobia.now.sh/badge?p=polka\" alt=\"install size\" /\u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003eA micro web server so fast, it'll make you dance! :dancers:\u003c/div\u003e\n\n\u003cbr /\u003e\n\n\u003e \u003cbr/\u003e**Black Lives Matter.** ✊🏽✊🏾✊🏿\u003cbr/\u003e\nSupport the [Equal Justice Initiative](https://support.eji.org/give/153413/#!/donation/checkout), [Campaign Zero](https://www.joincampaignzero.org/), or [Educate Yourself](https://blacklivesmatters.carrd.co/#educate).\u003cbr/\u003e\u0026nbsp;\n\n\u003cbr /\u003e\n\nPolka is an extremely minimal, highly performant Express.js alternative. Yes, you're right, Express is _already_ super fast \u0026 not _that_ big :thinking: \u0026mdash; but Polka shows that there was (somehow) room for improvement!\n\nEssentially, 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:\n\nAnd, of course, in mandatory bullet-point format:\n\n* 33-50% faster than Express for simple applications\n* Middleware support, including Express middleware you already know \u0026 love\n* Nearly identical application API \u0026 route pattern definitions\n* ~90 LOC for Polka, 120 including [its router](https://github.com/lukeed/trouter)\n\n\n## Install\n\n```\n$ npm install --save polka\n```\n\n## Usage\n\n```js\nconst polka = require('polka');\n\nfunction one(req, res, next) {\n  req.hello = 'world';\n  next();\n}\n\nfunction two(req, res, next) {\n  req.foo = '...needs better demo 😔';\n  next();\n}\n\npolka()\n  .use(one, two)\n  .get('/users/:id', (req, res) =\u003e {\n    console.log(`~\u003e Hello, ${req.hello}`);\n    res.end(`User: ${req.params.id}`);\n  })\n  .listen(3000, () =\u003e {\n    console.log(`\u003e Running on localhost:3000`);\n  });\n```\n\n## API\n\nPolka extends [Trouter](https://github.com/lukeed/trouter) which means it inherits its API, too!\n\n### polka(options)\n\nReturns an instance of Polka~!\n\n#### options.server\nType: `Server`\u003cbr\u003e\n\nA 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`.\n\nPolka _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`.\n\n\u003e **Important:** The `server` key will be `undefined` until `polka.listen` is invoked, unless a server was provided.\n\n#### options.onError\nType: `Function`\n\nA catch-all error handler; executed whenever a middleware throws an error. Change this if you don't like default behavior.\n\nIts signature is `(err, req, res, next)`, where `err` is the `String` or `Error` thrown by your middleware.\n\n\u003e **Caution:** Use `next()` to bypass certain errors **at your own risk!** \u003cbr\u003eYou must be certain that the exception will be handled elsewhere or _can_ be safely ignored. \u003cbr\u003eOtherwise your response will never terminate!\n\n#### options.onNoMatch\nType: `Function`\n\nA handler when no route definitions were matched. Change this if you don't like default behavior, which sends a `404` status \u0026 `Not found` response.\n\nIts signature is `(req, res)` and requires that you terminate the response.\n\n\n### use(base, ...fn)\n\nAttach [middleware(s)](#middleware) and/or sub-application(s) to the server. These will execute _before_ your routes' [handlers](#handlers).\n\n**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.\n\n#### base\nType: `String`\u003cbr\u003e\nDefault: `undefined`\n\nThe base path on which the following middleware(s) or sub-application should be mounted.\n\n#### fn\nType: `Function|Array`\n\nYou may pass one or more functions at a time. Each function must have the standardized `(req, res, next)` signature.\n\nYou may also pass a sub-application, which _must_ be accompanied by a `base` pathname.\n\nPlease see [`Middleware`](#middleware) and [Express' middleware examples](http://expressjs.com/en/4x/api.html#middleware-callback-function-examples) for more info.\n\n\n### parse(req)\n\nReturns: `Object` or `undefined`\n\nAs of `v0.5.0`, this is an alias of the [`@polka/url`](/packages/url) module. For nearly all cases, you'll notice no changes.\n\nBut, for whatever reason, you can quickly swap in [`parseurl`](https://github.com/pillarjs/parseurl) again:\n\n```js\nconst app = polka();\napp.parse = require('parseurl');\n//=\u003e Done!\n```\n\n### listen()\n\nReturns: `Polka`\n\nBoots (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.\n\nAs of `v0.5.0`, this method no longer returns a Promise. Instead, the current Polka instance is returned directly, allowing for chained operations.\n\n```js\n// Could not do this before 0.5.0\nconst { server, handler } = polka().listen();\n\n// Or this!\nconst app = polka().listen(PORT, onAppStart);\n\napp.use('users', require('./users'))\n  .get('/', (req, res) =\u003e {\n    res.end('Pretty cool!');\n  });\n```\n\n### handler(req, res, parsed)\n\nThe 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.\n\nIf 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.\n\n\u003e **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.\n\nAt 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 \u0026mdash; otherwise a `(404) Not found` response is returned, configurable via [`options.onNoMatch`](#optionsonnomatch).\n\n#### req\nType: `IncomingMessage`\n\n#### res\nType: `ServerResponse`\n\n#### parsed\nType: `Object`\n\nOptionally 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.\n\n\n## Routing\n\nRoutes are used to define how an application responds to varying HTTP methods and endpoints.\n\n\u003e If you're coming from Express, there's nothing new here!\u003cbr\u003e However, do check out [Comparisons](#comparisons) for some pattern changes.\n\n### Basics\n\nEach route is comprised of a [path pattern](#patterns), a [HTTP method](#methods), and a [handler](#handlers) (aka, what you want to do).\n\nIn code, this looks like:\n\n```js\napp.METHOD(pattern, handler);\n```\n\nwherein:\n\n* `app` is an instance of `polka`\n* [`METHOD`](#methods) is any valid HTTP/1.1 method, lowercased\n* [`pattern`](#patterns) is a routing pattern (string)\n* [`handler`](#handlers) is the function to execute when `pattern` is matched\n\nAlso, a single pathname (or `pattern`) may be reused with multiple METHODs.\n\nThe following example demonstrates some simple routes.\n\n```js\nconst app = polka();\n\napp.get('/', (req, res) =\u003e {\n  res.end('Hello world!');\n});\n\napp.get('/users', (req, res) =\u003e {\n  res.end('Get all users!');\n});\n\napp.post('/users', (req, res) =\u003e {\n  res.end('Create a new User!');\n});\n\napp.put('/users/:id', (req, res) =\u003e {\n  res.end(`Update User with ID of ${req.params.id}`);\n});\n\napp.delete('/users/:id', (req, res) =\u003e {\n  res.end(`CY@ User ${req.params.id}!`);\n});\n```\n\n### Patterns\n\nUnlike 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) \u0026 more memory efficient, this does also prevent complex pattern matching.\n\nHowever, 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:\n\n\u003e See [Comparisons](#comparisons) for the list of `RegExp`-based patterns that Polka does not support.\n\nThe supported pattern types are:\n\n* static (`/users`)\n* named parameters (`/users/:id`)\n* nested parameters (`/users/:id/books/:title`)\n* optional parameters (`/users/:id?/books/:title?`)\n* any match / wildcards (`/users/*`)\n\n### Parameters\n\nAny 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.\n\n\u003e **Important:** Your parameter names should be unique, as shared names will overwrite each other!\n\n```js\napp.get('/users/:id/books/:title', (req, res) =\u003e {\n  let { id, title } = req.params;\n  res.end(`User: ${id} \u0026\u0026 Book: ${title}`);\n});\n```\n\n```sh\n$ curl /users/123/books/Narnia\n#=\u003e User: 123 \u0026\u0026 Book: Narnia\n```\n\n### Methods\n\nAny valid HTTP/1.1 method is supported! However, only the most common methods are used throughout this documentation for demo purposes.\n\n\u003e **Note:** For a full list of valid METHODs, please see [this list](https://github.com/lukeed/trouter#method).\n\n### Handlers\n\nRequest 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).\n\nEvery route definition must contain a valid `handler` function, or else an error will be thrown at runtime.\n\n\u003e **Important:** You must _always_ terminate a `ServerResponse`!\n\nIt'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 \u0026 eventually exit with a `TIMEOUT` error.\n\n\u003e **Note:** This is a native `http` behavior.\n\n#### Async Handlers\n\nIf using Node 7.4 or later, you may leverage native `async` and `await` syntax! :heart_eyes_cat:\n\nNo special preparation is needed \u0026mdash; simply add the appropriate keywords.\n\n```js\nconst app = polka();\n\nconst sleep = ms =\u003e new Promise(r =\u003e setTimeout(r, ms));\n\nasync function authenticate(req, res, next) {\n  let token = req.headers['authorization'];\n  if (!token) return (res.statusCode=401,res.end('No token!'));\n  req.user = await Users.find(token); // \u003c== fake\n  next(); // done, woot!\n}\n\napp\n  .use(authenticate)\n  .get('/', async (req, res) =\u003e {\n    // log middleware's findings\n    console.log('~\u003e current user', req.user);\n    // force sleep, because we can~!\n    await sleep(500);\n    // send greeting\n    res.end(`Hello, ${req.user.name}`);\n  });\n```\n\n\n## Middleware\n\nMiddleware are functions that run in between (hence \"middle\") receiving the request \u0026 executing your route's [`handler`](#handlers) response.\n\n\u003e Coming from Express? Use any middleware you already know \u0026 love! :tada:\n\nThe middleware signature receives the request (`req`), the response (`res`), and a callback (`next`).\n\nThese can apply mutations to the `req` and `res` objects, and unlike Express, have access to `req.params`, `req.path`, `req.search`, and `req.query`!\n\nMost 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`.\n\n```js\n// Log every request\nfunction logger(req, res, next) {\n  console.log(`~\u003e Received ${req.method} on ${req.url}`);\n  next(); // move on\n}\n\nfunction authorize(req, res, next) {\n  // mutate req; available later\n  req.token = req.headers['authorization'];\n  req.token ? next() : ((res.statusCode=401) \u0026\u0026 res.end('No token!'));\n}\n\npolka().use(logger, authorize).get('*', (req, res) =\u003e {\n  console.log(`~\u003e user token: ${req.token}`);\n  res.end('Hello, valid user');\n});\n```\n\n```sh\n$ curl /\n# ~\u003e Received GET on /\n#=\u003e (401) No token!\n\n$ curl -H \"authorization: secret\" /foobar\n# ~\u003e Received GET on /foobar\n# ~\u003e user token: secret\n#=\u003e (200) Hello, valid user\n```\n\n### Middleware Sequence\n\nIn Polka, middleware functions are organized into tiers.\n\nUnlike Express, Polka middleware are tiered into \"global\", \"filtered\", and \"route-specific\" groups.\n\n* Global middleware are defined via `.use('/', ...fn)` or `.use(...fn)`, which are synonymous.\u003cbr\u003e_Because_ every request's `pathname` begins with a `/`, this tier is always triggered.\n\n* 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.\u003cbr\u003eThese functions will execute _after_ \"global\" middleware but before the route-specific handler.\n\n* Route handlers match specific paths and execute last in the chain. They must also match the `method` action.\n\nOnce 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.\n\nContrast this with Express, which does not tier your middleware and instead iterates through your entire application in the sequence that you composed it.\n\n```js\n// Express\nexpress()\n  .get('/', get)\n  .use(foo)\n  .get('/users/123', user)\n  .use('/users', users)\n\n// Polka\nPolka()\n  .get('/', get)\n  .use(foo)\n  .get('/users/123', user)\n  .use('/users', users)\n```\n\n```sh\n$ curl {APP}/\n# Express :: [get]\n# Polka   :: [foo, get]\n\n$ curl {APP}/users/123\n# Express :: [foo, user]\n# Polka   :: [foo, users, user]\n```\n\n\n### Middleware Errors\n\nIf an error arises within a middleware, the loop will be exited. This means that no other middleware will execute \u0026 neither will the route handler.\n\nSimilarly, regardless of `statusCode`, an early response termination will also exit the loop \u0026 prevent the route handler from running.\n\nThere are three ways to \"throw\" an error from within a middleware function.\n\n\u003e **Hint:** None of them use `throw` :joy_cat:\n\n1. **Pass any string to `next()`**\n\n    This will exit the loop \u0026 send a `500` status code, with your error string as the response body.\n\n    ```js\n    polka()\n      .use((req, res, next) =\u003e next('💩'))\n      .get('*', (req, res) =\u003e res.end('wont run'));\n    ```\n\n    ```sh\n    $ curl /\n    #=\u003e (500) 💩\n    ```\n\n2. **Pass an `Error` to `next()`**\n\n    This is similar to the above option, but gives you a window in changing the `statusCode` to something other than the `500` default.\n\n    ```js\n    function oopsies(req, res, next) {\n      let err = new Error('Try again');\n      err.code = 422;\n      next(err);\n    }\n    ```\n\n    ```sh\n    $ curl /\n    #=\u003e (422) Try again\n    ```\n\n3. **Terminate the response early**\n\n    Once the response has been ended, there's no reason to continue the loop!\n\n    This approach is the most versatile as it allows to control every aspect of the outgoing `res`.\n\n    ```js\n    function oopsies(req, res, next) {\n      if (true) {\n        // something bad happened~\n        res.writeHead(400, {\n          'Content-Type': 'application/json',\n          'X-Error-Code': 'Please dont do this IRL'\n        });\n        let json = JSON.stringify({ error:'Missing CSRF token' });\n        res.end(json);\n      } else {\n        next(); // never called FYI\n      }\n    }\n    ```\n\n    ```sh\n    $ curl /\n    #=\u003e (400) {\"error\":\"Missing CSRF token\"}\n    ```\n\n\n## Benchmarks\n\nQuick comparison between various frameworks using [`wrk`](https://github.com/wg/wrk) on `Node v10.4.0`.\u003cbr\u003e Results are taken with the following command, after one warm-up run:\n\n```\n$ wrk -t4 -c4 -d10s http://localhost:3000/users/123\n```\n\nAdditional benchmarks between Polka and Express (using various Node versions) can be [found here](/bench).\n\n\u003e **Important:** Time is mostly spent in _your application code_ rather than Express or Polka code!\u003cbr\u003e Switching from Express to Polka will (likely) not show such drastic performance gains.\n\n```\nNative\n    Thread Stats   Avg      Stdev     Max   +/- Stdev\n        Latency     1.96ms  119.06us   5.33ms   92.57%\n        Req/Sec    12.78k   287.46    13.13k    90.00%\n      508694 requests in 10.00s, 50.45MB read\n    Requests/sec:  50867.22\n    Transfer/sec:      5.05MB\n\nPolka\n    Thread Stats   Avg      Stdev     Max   +/- Stdev\n        Latency     1.98ms  119.26us   4.45ms   92.87%\n        Req/Sec    12.68k   287.74    13.05k    94.06%\n      509817 requests in 10.10s, 50.56MB read\n    Requests/sec:  50475.67\n    Transfer/sec:      5.01MB\n\nRayo\n    Thread Stats   Avg      Stdev     Max   +/- Stdev\n        Latency     2.02ms  116.55us   6.66ms   92.55%\n        Req/Sec    12.43k   262.32    12.81k    91.58%\n      499795 requests in 10.10s, 49.57MB read\n    Requests/sec:  49481.55\n    Transfer/sec:      4.91MB\n\nFastify\n    Thread Stats   Avg      Stdev     Max   +/- Stdev\n        Latency     2.10ms  138.04us   5.46ms   91.50%\n        Req/Sec    11.96k   414.14    15.82k    95.04%\n      479518 requests in 10.10s, 66.31MB read\n    Requests/sec:  47476.75\n    Transfer/sec:      6.57MB\n\nKoa\n    Thread Stats   Avg      Stdev     Max   +/- Stdev\n        Latency     2.95ms  247.10us   6.91ms   72.18%\n        Req/Sec     8.52k   277.12     9.09k    70.30%\n      342518 requests in 10.10s, 47.36MB read\n    Requests/sec:  33909.82\n    Transfer/sec:      4.69MB\n\nExpress\n    Thread Stats   Avg      Stdev     Max   +/- Stdev\n        Latency     4.91ms  484.52us  10.65ms   89.71%\n        Req/Sec     5.11k   350.75     9.69k    98.51%\n      204520 requests in 10.10s, 40.57MB read\n    Requests/sec:  20249.80\n    Transfer/sec:      4.02MB\n```\n\n\n## Comparisons\n\nPolka'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:\n\nThere are, however, a few main differences. Polka does not support or offer:\n\n1) **Polka uses a tiered middleware system.**\n\n    Express maintains the sequence of your route \u0026 middleware declarations during its runtime, which can pose a problem when composing sub-applications. Typically, this forces you to duplicate groups of logic.\n\n    Please see [Middleware Sequence](#middleware-sequence) for an example and additional details.\n\n2) **Any built-in view/rendering engines.**\n\n    Most templating engines can be incorporated into middleware functions or used directly within a route handler.\n\n3) **The ability to `throw` from within middleware.**\n\n    However, all other forms of middleware-errors are supported. (See [Middleware Errors](#middleware-errors).)\n\n    ```js\n    function middleware(res, res, next) {\n      // pass an error message to next()\n      next('uh oh');\n\n      // pass an Error to next()\n      next(new Error('🙀'));\n\n      // send an early, customized error response\n      res.statusCode = 401;\n      res.end('Who are you?');\n    }\n    ```\n\n4) **Express-like response helpers... yet! (#14)**\n\n    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_)\n\n5) **`RegExp`-based route patterns.**\n\n    Polka's router uses string comparison to match paths against patterns. It's a lot quicker \u0026 more efficient.\n\n    The following routing patterns **are not** supported:\n\n    ```js\n    app.get('/ab?cd', _ =\u003e {});\n    app.get('/ab+cd', _ =\u003e {});\n    app.get('/ab*cd', _ =\u003e {});\n    app.get('/ab(cd)?e', _ =\u003e {});\n    app.get(/a/, _ =\u003e {});\n    app.get(/.*fly$/, _ =\u003e {});\n    ```\n\n    The following routing patterns **are** supported:\n\n    ```js\n    app.get('/users', _ =\u003e {});\n    app.get('/users/:id', _ =\u003e {});\n    app.get('/users/:id?', _ =\u003e {});\n    app.get('/users/:id/books/:title', _ =\u003e {});\n    app.get('/users/*', _ =\u003e {});\n    ```\n\n6) **Polka instances are not (directly) the request handler.**\n\n    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.\n\n    In the Polka-sphere, this functionality lives in your application's [`handler`](#handlerreq-res-parsed) instead.\n\n    Here's an example with [`supertest`](https://github.com/visionmedia/supertest), a popular testing utility for Express apps.\n\n    ```js\n    const request = require('supertest');\n    const send = require('@polka/send-type');\n\n    const express = require('express')();\n    const polka = require('polka')();\n\n    express.get('/user', (req, res) =\u003e {\n      res.status(200).json({ name: 'john' });\n    });\n\n    polka.get('/user', (req, res) =\u003e {\n      send(res, 200, { name: 'john' });\n    });\n\n    function isExpected(app) {\n      request(app)\n        .get('/user')\n        .expect('Content-Type', /json/)\n        .expect('Content-Length', '15')\n        .expect(200);\n    }\n\n    // Express: Pass in the entire application directly\n    isExpected(express);\n\n    // Polka: Pass in the application `handler` instead\n    isExpected(polka.handler);\n    ```\n\n\n## License\n\nMIT © [Luke Edwards](https://lukeed.com)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flukeed%2Fpolka","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flukeed%2Fpolka","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flukeed%2Fpolka/lists"}