{"id":16488952,"url":"https://github.com/schamberg97/restana-express","last_synced_at":"2026-01-21T02:03:24.661Z","repository":{"id":42729826,"uuid":"279554721","full_name":"schamberg97/restana-express","owner":"schamberg97","description":"Express compatibility layer for Restana","archived":false,"fork":false,"pushed_at":"2023-01-24T03:31:04.000Z","size":315,"stargazers_count":2,"open_issues_count":13,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-09-21T22:54:27.640Z","etag":null,"topics":["compatibility","compatibility-layer","express","middleware","restana"],"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/schamberg97.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-07-14T10:30:43.000Z","updated_at":"2023-06-16T13:25:15.000Z","dependencies_parsed_at":"2023-01-31T16:30:27.952Z","dependency_job_id":null,"html_url":"https://github.com/schamberg97/restana-express","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/schamberg97/restana-express","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schamberg97%2Frestana-express","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schamberg97%2Frestana-express/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schamberg97%2Frestana-express/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schamberg97%2Frestana-express/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/schamberg97","download_url":"https://codeload.github.com/schamberg97/restana-express/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schamberg97%2Frestana-express/sbom","scorecard":{"id":803733,"data":{"date":"2025-08-11","repo":{"name":"github.com/schamberg97/restana-express","commit":"c7524c64d87991e9326a8fa785ddc2d23a5bd440"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.7,"checks":[{"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":"Pinned-Dependencies","score":3,"reason":"dependency not pinned by hash detected -- score normalized to 3","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/npm-publish.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/schamberg97/restana-express/npm-publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/npm-publish.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/schamberg97/restana-express/npm-publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/npm-publish.yml:25: update your workflow using https://app.stepsecurity.io/secureworkflow/schamberg97/restana-express/npm-publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/npm-publish.yml:26: update your workflow using https://app.stepsecurity.io/secureworkflow/schamberg97/restana-express/npm-publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/npm-publish.yml:39: update your workflow using https://app.stepsecurity.io/secureworkflow/schamberg97/restana-express/npm-publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/npm-publish.yml:40: update your workflow using https://app.stepsecurity.io/secureworkflow/schamberg97/restana-express/npm-publish.yml/master?enable=pin","Info:   0 out of   6 GitHub-owned GitHubAction dependencies pinned","Info:   3 out of   3 npmCommand 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":"Code-Review","score":0,"reason":"Found 0/22 approved changesets -- score normalized to 0","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":"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":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/npm-publish.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":"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":"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":"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":"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":"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":"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 'master'"],"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"}},{"name":"Vulnerabilities","score":0,"reason":"34 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-fwr7-v2mv-hh25","Warn: Project is vulnerable to: GHSA-qwcr-r2fm-qrc7","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-257v-vj4p-3w2h","Warn: Project is vulnerable to: GHSA-pxg6-pf52-xh8x","Warn: Project is vulnerable to: GHSA-h452-7996-h45h","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c","Warn: Project is vulnerable to: GHSA-rv95-896h-c2vc","Warn: Project is vulnerable to: GHSA-qw6h-vgh9-j6wx","Warn: Project is vulnerable to: GHSA-2j2x-2gpw-g8fm","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-4q6p-r6v2-jvc5","Warn: Project is vulnerable to: GHSA-ww39-953v-wcq6","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-9wv6-86v2-598j","Warn: Project is vulnerable to: GHSA-rhx6-c78j-4q9w","Warn: Project is vulnerable to: GHSA-g6ww-v8xp-vmwg","Warn: Project is vulnerable to: GHSA-p493-635q-r6gr","Warn: Project is vulnerable to: GHSA-3965-hpx2-q597","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-m6fv-jmcg-4jfg","Warn: Project is vulnerable to: GHSA-cm22-4g7w-348p","Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6","Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7","Warn: Project is vulnerable to: GHSA-c4w7-xm78-47vh"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-23T11:13:40.584Z","repository_id":42729826,"created_at":"2025-08-23T11:13:40.584Z","updated_at":"2025-08-23T11:13:40.584Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28622472,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-20T23:49:58.628Z","status":"online","status_checked_at":"2026-01-21T02:00:08.227Z","response_time":86,"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":["compatibility","compatibility-layer","express","middleware","restana"],"created_at":"2024-10-11T13:40:29.135Z","updated_at":"2026-01-21T02:03:24.643Z","avatar_url":"https://github.com/schamberg97.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Introduction\n\nWARNING: this project is in its infancy, test hard before using in production\n\nRestana-express is a partial Express compatibility layer for Restana, which implements most of the (req, res) additions over native node.js http(s) server made in Express. It may also work with other frameworks, but this is not guaranteed and may require slight modification. \n\nTo achieve this, restana-express imports some methods and properties from Express (if possible) or reimplements them.\n\nRestana-express-compatibility does not aim to offer a 100% compatibility with Express and offers only most of the methods and properties outlined on the following pages:\n\n[Express Res](https://expressjs.com/en/4x/api.html#res)\n\n[Express Req](https://expressjs.com/en/4x/api.html#req)\n\n\nDespite this, testing is *mostly* done with modified Express tests.\nHowever, please see the table in Compatibility section\n\n# Version \u0026 Changes\n\nPlease see `CHANGELOG.md`\n\n# License\n\nMIT License\n\n# Usage\n\n\n## Installation\n\n```\nnpm i --save restana-express-compatibility\n```\n\n## Setting up the restana and middleware\n\n```js\n\tconst restana = require('restana')\n\tlet restanaExpressCompatibilityMod = require('restana-express-compatibility')\n\tlet compatibilityLayerSettings = {\n\t\tres: {\n\t\t\ttoUse: ['all'], // you can specify which res components you want. However, do take note that you may run into issues, if the component you use depends on another one, which you haven't specified. Check dependencies table in Observations and known problems\n\t\t\ttoDisable: [], // Array of methods and properties to disable. Overrides 'toUse'\n\t\t\trender: {  // res.render was completely reimplemented and is now setup differently\n\t\t\t\tviewsDir: path.resolve(__dirname + \"/views/\"),\n\t\t\t\trenderExt: '.pug',\n\t\t\t\trenderEngine: 'pug',\n\t\t\t\trenderFunction: \"__express\"\n\t\t\t}\n\t\t},\n\t\treq: {\n\t\t\ttoUse: ['all'], // you can specify which req components you want. However, do take note that you may run into issues, if the component you use depends on another one, which you haven't specified. Check dependencies table in Observations and known problems \n\t\t\ttoDisable: [], // Array of methods and properties to disable. Overrides 'toUse'\n\t\t\tproxyTrust: true, // express proxyTrust-related functions have been reimplemented, so it is setup here. Accepts Number, function, String or Array of IPs, Boolean (see Express proxy settings)\n\t\t\tqueryParser: true, // true, \"simple\" uses restana's url query parser, \"extended\" or undefined enables Express's extended query parser (Check express docs for explanation)\n\t\t\tsubdomainsOffset: 0, // defaults to 2\n\t\t\t// OR\n\t\t\tsubdomainOffset: 0, // subdomainsOffset is still prioritized\n\t\t\tpropertiesAsFunctions: true, // default - undefined. False to disable, true to enable\n\t\t\tetag: {\n\t\t\t\ttype: 'tiny', // default - tiny, true enables Express's weak ETag, otherwise uses the same options as Express (see - https://expressjs.com/en/api.html#etag.options.table)\n\t\t\t\tseed: 01234567890, // Number, for use with Tiny ETag. Should be syncronised between all instances of your server on all machines, otherwise useless, since all ETags will be different. However, will generate a pseudo-random number by default instead.\n\t\t\t\tmaxSize: 1000 // Used with Tiny ETag, size of internal cache (number of entries). 1000 is the default. You should probably set closer towards the number of resources available on your service. \n\t\t\t}\n\t\t}\n\t}\n\t\n\tvar app = restana();\n\n\tlet restanaExpressCompatibility = new restanaExpressCompatibilityMod(compatibilityLayerSettings)\n\n\tapp.use(restanaExpressCompatibility.middleware)\n\n\tapp.get('/hi/', (req, res) =\u003e {\n\t\tres.send({\n\t\t\tmsg: 'Hello World!',\n\t\t\tquery: req.query,\n\t\t\tsubdomains: req.subdomains,\n\t\t\tip: req.ip,\n\t\t})\n\t})\n\n\tlet server = app.start(3002, '0.0.0.0')\n\t  \n```\n\n# Compatibility \n\n## Res\n\n### Properties\n\n| Property or object | Implemented? | Notes |\n| --- | --- | --- |\n| locals | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| headersSent | \u003cspan style=\"color:green\"\u003e Native \u003c/span\u003e | Doesn't need reimplementation |\n| app | \u003cspan style=\"color:red\"\u003e No \u003c/span\u003e | Probably not going to be implemented |\n\n### Methods\n\n| Method or object | Implemented? | Notes |\n| --- | --- | --- |\n| append | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| attachment | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| cookie | \u003cspan style=\"color:yellow\"\u003e Yes \u003c/span\u003e | cookieParser required for signed cookies, check req.cookie entry in req properties |\n| clearCookie | \u003cspan style=\"color:yellow\"\u003e Yes \u003c/span\u003e | cookieParser required for signed cookies, check req.cookie entry in req properties |\n| download | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| end | \u003cspan style=\"color:green\"\u003e Native \u003c/span\u003e | Doesn't need reimplementation |\n| get | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| json and jsonp | \u003cspan style=\"color:yellow\"\u003e Partial \u003c/span\u003e | Reimplemented, but very crudely. It only JSON stringifies the object supplied. Needs to be rewritten |\n| links | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| location | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| redirect | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| render | \u003cspan style=\"color:yellow\"\u003e Rewritten from scratch \u003c/span\u003e | Rewritten from scratch, compatible with using only one render engine. Workarounds might be possible. Please read section on res.render |\n| send | \u003cspan style=\"color:yellow\"\u003e Partial  \u003c/span\u003e | Slightly modifies restana's res.send to make it respect change of http status code through res.status(code) or res.statusCode = code |\n| sendFile | \u003cspan style=\"color:yellow\"\u003e Partial \u003c/span\u003e | Not sufficiently covered by tests, not all Express tests are ported. Likely works just fine |\n| sendStatus | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| set / header | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| status | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| type / contentType | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| vary | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| format | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | Not sufficiently covered by tests |\n\n\n### res.render\n\nrestana-express does not import res.render, because it depends too much on express core. Therefore, this project partially reimplements the res.render itself.\n\n#### Limitations\n\n* Works only with one render engine, you can't specify many (workarounds might be possible by initializing this middleware several times differently in various places of your project)\n* No express automagic :(\n\n#### Settings\n\n```js\nlet compatibilityLayerSettings = {\n\tres: {\n\t\ttoUse: ['all'], // should either be an Array with 'all' or at least include 'render'\n\t\trender: {  // res.render was completely reimplemented and is now setup differently\n\t\t\tviewsDir: path.resolve(__dirname + \"/views/\"), // ESSENTIAL parameter, without it res.render will send an error\n\t\t\trenderExt: '.pug', // essential, if you use something besides Pug. Dot at the beginning is needed. Defaults to '.pug'\n\t\t\trenderEngine: 'pug', // name of engine. restana-express will require it. Defaults to 'pug'\n\t\t\trenderFunction: \"__express\" // Internal function that works with express. Usually you should leave it unchanged. Defaults to '__express'\n\t\t}\n\t},\n\t...\n}\n```\n\n## Req\n\n### Properties\n\n| Property or object | Implemented? | Notes |\n| --- | --- | --- |\n| app | \u003cspan style=\"color:red\"\u003e No \u003c/span\u003e | Probably not going to be implemented |\n| baseUrl | \u003cspan style=\"color:red\"\u003e No \u003c/span\u003e | Help is welcome |\n| headersSent | \u003cspan style=\"color:green\"\u003e Native \u003c/span\u003e | Doesn't need reimplementation |\n| body | \u003cspan style=\"color:red\"\u003e No \u003c/span\u003e | You may use body-parser middleware, since this is what express uses internally. You can app.use (or abuse :D) it either before or immediately after restana-express |\n| cookies | \u003cspan style=\"color:red\"\u003e No, not in restana-express itself. Please, see notes \u003c/span\u003e | You should use cookie-parser middleware, as this property is not implemented in Express itself. |\n| fresh | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| hostname | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| ip | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| ips | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| method | \u003cspan style=\"color:green\"\u003e Native \u003c/span\u003e | Does not need reimplementation |\n| originalUrl | \u003cspan style=\"color:green\"\u003e Native \u003c/span\u003e | Does not need reimplementation |\n| path | \u003cspan style=\"color:green\"\u003e Native \u003c/span\u003e | Does not need reimplementation |\n| protocol | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| query | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | Set up the type of parser through compatibilityLayerSettings.req.queryParser, before initializing and app.use'ing the middleware. Accepts same values as express for this setting |\n| hostname | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| route | \u003cspan style=\"color:red\"\u003e No \u003c/span\u003e | Help is welcome |\n| secure | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| signedCookies | \u003cspan style=\"color:red\"\u003e No, not in restana-express itself. Please, see notes \u003c/span\u003e | Check entry in this table on req.cookies |\n| stale | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| subdomains | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | subdomain offset is set either through compatibilityLayerSettings.req.subdomainsOffset OR compatibilityLayerSettings.req.subdomainOffset |\n| xhr | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n\n\n### Methods\n\n| Method or object | Implemented? | Notes |\n| --- | --- | --- |\n| accepts | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| acceptsCharsets | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| acceptsEncodings | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| acceptsLanguages | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| get / header | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| is | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n| param | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | \u003cspan style=\"color:yellow\"\u003e Deprecated by express. Please note that you need body-parser middleware \u003c/span\u003e |\n| range | \u003cspan style=\"color:green\"\u003e Yes \u003c/span\u003e | N/A |\n\n\n## Serve static\n\nFor serving static content, see this article: https://thejs701816742.wordpress.com/2019/07/12/restana-static-serving-the-frontend-with-node-js-beyond-nginx/\n\nIt is recommended that you limit these middleware to some routes, while restana-express-compatibility to other routes.\n\n## Observations and known problems\n\n### Tests\n\nTests currently cover only the default options, at which restana-express-compatibility is the most compatible with Express, but also the slowest. Help is welcome.\n\n### Restana-express-compatibility and cookie- and body-parser\n\nIn order for this middleware to work correctly for routes or middleware that were designed for Express, you need to speicfy and app.use it before them. Do take note, though, that you need to enable cookie-parser middleware before restana-express, if you plan to use req.secureCookies and send secure cookies:\n\n```js\n\nvar cookieParser = require('cookie-parser');\n\n...\napp.use(cookieParser(process.env.COOKIE_PARSER_SECRET || \"wonderland\"));\napp.use(restanaExpressCompatibility.middleware)\n\n```\n\nThe same applies to body-parser: \n\n```js\n\nvar cookieParser = require('cookie-parser');\nconst bodyParser = require('body-parser')\n\n\n...\n// parse application/json\napp.use(bodyParser.json())\napp.use(cookieParser(process.env.COOKIE_PARSER_SECRET || \"wonderland\"));\napp.use(restanaExpressCompatibility.middleware)\n\n```\n\n### Performance\n\nWith the default settings, restana-express-compatibility is highly compatible with express. This compatibility comes at a great cost - the performance is higher than with express, but not THAT much higher (only by up to 60%, depending on task). In other words, restana-express-compatibility can handle 16-17k requests per second instead of 10-11k with express with /json/ route (see benchmarks section)\n\nHere are the tips to increase performance:\n\n#### Tip 1 - Disable req properties, if you don't use them [EASY]\n\nSome express req properties are very convenient at times. These include:\n\n```js \n\t['protocol', 'ip', 'ips', 'hostname', 'subdomains', 'fresh', \"stale\", 'xhr', 'secure'] // the list is exhaustive\n```\n\nHowever, before continuing with your application logic, each of them has to be processed, EVEN IF you don't end up using them. Internally, there are functions, like req.protocolPropFn which is run in the middleware, before carrying on to your routes. In this example, req.protocolPropFn populates req.protocol. In the end, it means that middleware is forced to traverse through these functions and then run each one that is enabled.\n\nAs such, if performance is vital, you can either disable some properties (very modest performance increase) OR disable them altogether (great speed increase). Alternatively, you can turn them into functions instead:\n\n```js\nlet compatibilityLayerSettings = {\n\treq: {\n\t\tpropertiesAsFunctions: true,\n\t}\n}\n```\n\nThis way, whenever you need to know the protocol, instead of accessing req.protocol property, you'll need to call req.protocol() function. No arguments are required. This measure will boost restana-express-compatibility from handling 16-17k req/s to handling 21-23 req/s, which is a further 35% speed increase. You should be able to easily replace calls to req properties with calls to the new functions with your IDE or shell commands.\n\n#### Tip 2 - Don't enable extended query parsing or weak or strong ETag [MEDIUM]\n\nBy default, restana-express-compatibility uses a different implementation of ETag, which should work just fine. Don't change the ETag setting, unless you know what you are doing.\nThe same applies to query parsing - by default, Express.JS extended query parsing is used for Express compatibility reasons. We advise you to disable it and use the simple parser, as it is built-in, unless you REALLY need the extended parser. But in any case, you should avoid passing data as a query due to its inherent limitations.\n\nYou can also consider benchmarking with ETag disabled on specific routes with setting `res.locals.NO_ETAG = true` in your route or middleware. Beware that it may affect caching, if it is used.\n\n#### Tip 3 - disable what you don't need [HARD]\n\nBy disabling things you don't need, you make the server use less RAM, which is beneficial in the long term. Additionally, the middleware merges the req and res objects with objects containing functions and properties taken from Express each time a route after compatibility middleware is requested by a client. This means that for every unnecessary method or property activated, the server spends more time merging the object with Object.assign.\n\nFor instance, with no methods or properties activated, the merge takes only ~5000 nanoseconds. However, with all methods or properties, it takes ~60000 nanoseconds. That is more than a third of the total middleware working time (with req.properties converted into functions). This means that keeping bare minimum of methods and properties you need will make the middleware work faster. Therefore, your own code will execute faster. \n\nFor your convenience, there are dependency tables provided after the tips. Take note that number of dependencies may be reduced in the next minor release.\n\n#### Tip 4 - gradually stop using this module [POSSIBLY HARD]\n\nBy disabling this module and finding alternatives for your needs (if possible), you are going to have a much faster application. It is highly likely you do not need all of the methods and properties offered by express and only need a subset. Find appropriate middleware for your needs or reimplement the required methods to suit your needs. Your app performance is gonna thank you. Moreover, you can limit the middleware only to specific routes, so that it is not run, where it is not needed.\n\n\n#### Dependency tables\n\nPlease take note that these tables serve as an indication only. It is highly possible some mistakes could have been made, especially with Dependents column\n\n##### Res properties\n\n| Property or object | Can be disabled? | Dependencies |\n| --- | --- | --- |\n| locals | No | None |\n\n##### Res methods\n\n| Method or object | Can be disabled? | Dependencies | Dependents |\n| --- | --- | --- | --- |\n| send | No | None | Irrelevant |\n| append | Yes | res.get, res.set | res.cookie |\n| attachment | Yes | res.type, res.set | None |\n| cookie | Yes | res.append, cookie-parse middleware (for signed cookies, see Compatibility and Observations and known problems sections ) | res.clearCookie |\n| clearCookie | Yes | res.cookie and its dependencies | None |\n| download | Yes | res.sendFile | None |\n| end | No, native | None | Irrelevant |\n| get | Yes | None | res.append, REQ.fresh, REQ.hostname |\n| json and jsonp | Yes | None | res.render |\n| links | Yes | res.set | None |\n| location | Yes | REQ.get \u0026\u0026 res.set | res.redirect | \n| redirect | Yes | res.location, res.format, res.set | None |\n| render | Yes | res.status, res.json, res.set | None |\n| sendFile | Yes | None | None |\n| sendStatus | Yes | None | None |\n| set / header | Yes | None | res.append, res.attachment, res.links, res.location, res.redirect, res.render, res.type/res.contentType, res.format |\n| status | Yes | None | res.render |\n| type / contentType | Yes | res.set | res.attachment |\n| vary | Yes | None | res.format |\n| format | Yes | res.vary, res.set | res.redirect |\n\n##### Req properties\n\n| Property or object | Can be disabled? | Dependencies | Dependents |\n| --- | --- | --- | --- |\n| headersSent | No, Native | None | Irrelevant |\n| fresh | Yes | RES.get | req.stale, RES.send (soft-fail, ETag functionality gets disabled) |\n| ip | Yes | None | None |\n| ips | Yes | None | None | \n| method | No, Native | None | Irrelevant |\n| originalUrl | No, Native | None | Irrelevant |\n| params | No, Native | None | Irrelevant |\n| path | No, Native | None | Irrelevant |\n| protocol | Yes | req.get | req.secure |\n| query | No | None | Irrelevant |\n| hostname | Yes | req.get | req.subdomains |\n| secure | Yes | req.protocol | None |\n| stale | Yes | req.fresh | None |\n| subdomains | Yes | req.hostname | None |\n| xhr | Yes | req.get | None |\n\n#### Req methods\n\n| Method or object | Can be disabled? | Dependencies | Dependents |\n| --- | --- | --- | --- |\n| accepts | Yes | None | None | \n| acceptsCharsets | Yes | None | None | \n| acceptsEncodings | Yes | None | None | \n| acceptsLanguages | Yes | None | None | \n| get / header | Yes | None | req.range, RES.location, req.protocol, req.hostname, req.xhr |\n| is | Yes | None | None |\n| param | Yes | req.params, req.body, req.query | None | \n| range | Yes | req.get | None |\n\n\n\n# Benchmarks (not scientific)\n\n\n## Route setup\n\nThe following route is used: \n\n```js\n\napp.get('/hi/', async (req, res) =\u003e {\n\tres.send({\n\t  msg: 'Hello World!',\n\t  query: req.query\n\n\t})\n})\n\n```\n\nFor restana without restana-express, the following middleware is added:\n\n```js\n\napp.use((req,res,next) =\u003e {\n\tres.json = function(inp) {\n\t\tres.setHeader('Content-Type', 'application/json');\n\t\tif (inp == null) return res.send(null)\n\t\treturn res.send(JSON.stringify(inp))\n\t}\n\treturn next()\n})\n\n```\n\n## Tool used\n\n```\nnode ./performance/APP.js\nsleep 5\nwrk -t8 -c64 -d5s http://localhost:PORT/hi/\n\n```\n\n, where PORT and APP are changed to: \n\n* 3001 and restana for restana\n* 3002 and restanaExpressHighPerf for restana + restana-express (performance conscious, query-parser set to simple, req.properties turned into methods)\n* 3003 and restanaExpress for restana + restana-express (default)\n* 3004 and express for express\n\n\n## Hardware used\n\n\u003e MacBook Pro 2019, 2,4 GHz Intel Core i9, 64 GB 2667 MHz DDR4  \n\n## Results\n\n| Server | Result, req/s | Gains over express |\n|---|---|---|\n| Restana | 48866.22 | 315% |\n| Restana + restana-express (performance conscious) | 23014.65 | 96% |\n| restana + restana-express (default) | 19087.31 | 62% |\n| Express | 11752.61 | 0% |","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fschamberg97%2Frestana-express","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fschamberg97%2Frestana-express","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fschamberg97%2Frestana-express/lists"}