├── .gitignore ├── LICENSE ├── README.md └── docs ├── Guides ├── Benchmarking.md ├── Contributing.md ├── Database.md ├── Delay-Accepting-Requests.md ├── Ecosystem.md ├── Fluent-Schema.md ├── Getting-Started.md ├── Index.md ├── Migration-Guide-V3.md ├── Migration-Guide-V4.md ├── Plugins-Guide.md ├── Prototype-Poisoning.md ├── Recommendations.md ├── Serverless.md ├── Style-Guide.md ├── Testing.md └── Write-Plugin.md ├── Reference ├── ContentTypeParser.md ├── Decorators.md ├── Encapsulation.md ├── Errors.md ├── HTTP2.md ├── Hooks.md ├── Index.md ├── LTS.md ├── Lifecycle.md ├── Logging.md ├── Middleware.md ├── Plugins.md ├── Reply.md ├── Request.md ├── Routes.md ├── Server.md ├── Type-Providers.md ├── TypeScript.md └── Validation-and-Serialization.md └── index.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Fastify 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > [!TIP] 2 | > This project is archived. The website can be browsed using automatic translators like [Google Translate](https://fastify-dev.translate.goog/?_x_tr_sl=en&_x_tr_tl=zh-TW&_x_tr_hl=it&_x_tr_pto=wapp). 3 | 4 | 5 | # Fastify 한글 6 | 7 | Fastify 공식 문서 한글 번역 프로젝트입니다. (v4.2.1) 8 | 9 | ### 기여 10 | 11 | 기여를 환영합니다! 12 | 13 | 1. 번역하고자 하는 문서를 이슈에 등록하세요. (번역 되었는지/진행 중인지 확인 필수) 14 | 2. 본 레포지토리를 fork하세요. 15 | 3. 포크한 레포지토리에 브랜치를 만들어 번역을 진행하세요. 16 | 4. PR을 요청하세요. 17 | 5. 리뷰를 거쳐 merge됩니다. 18 | 19 | https://github.com/seia-soto/fastify-kr 에서 마이그레이션 중 20 | 21 | ### 번역 가이드라인 22 | 23 | 반드시 아래 사항을 모두 지켜야 merge되는 것은 아닙니다. 24 | 25 | 문맥에 따라 역자의 판단을 우선할 수 있습니다. 하지만 가이드라인을 인지하고, 고려하여 번역해주시기 바랍니다. 26 | 27 | 1. 가능한 피동형 보다는 능동형을 사용합니다. 28 | 29 | ``` 30 | ex. 제거될(x), 제거할(o) 31 | 사용될(x), 사용할(o) 32 | ``` 33 | 34 | 2. 예제 코드에서 그대로 사용하는 기술 용어이거나, 널리 사용하는 개발 용어인 경우 번역하지 않아도 무방합니다. 그러나 하나의 아티클에서는 되도록 통일성을 유지해주세요. 35 | 자주 등장하는 용어에 관한 번역 사례는 [번역 용어집](https://github.com/fastify/fastify-korean/wiki/%EB%B2%88%EC%97%AD-%EC%9A%A9%EC%96%B4%EC%A7%91)을 참고하세요. 단어 추가를 제안하려면 이슈로 등록해주세요. 36 | 37 | ``` 38 | ex. scope -> 스코프(o), 범위(o) 39 | body -> 바디(o), 본문(o) 40 | parsing -> 파싱(o), 구문 분석(o) 41 | ``` 42 | 43 | 3. '하십시오(합쇼)체'를 기본으로 하되 ‘해요체'를 섞어 써도 무방합니다. 다만 해요체를 쓸 때는 경어를 사용합니다. 44 | 45 | ``` 46 | ex. ‘-합니다.’ (o), ‘-하세요’ (o), ‘해요.’ (x) 47 | ``` 48 | 49 | 4. '해요체'를 쓸 때를 제외하고, 높임말 `-시`는 사용하지 않습니다. 50 | 51 | ``` 52 | ex. '사용하기를'(o) 53 | '사용하시기를'(x) 54 | ``` 55 | 56 | 5. 가능한 복수형 보다는 단수형을 사용합니다. 57 | 58 | ``` 59 | ex. 예제들을(x), 예제를(o) 60 | 객체들에(x), 객체에(o) 61 | ``` 62 | 63 | 번역에 관한 제안 사항은 이슈에 등록해주세요. 64 | 65 | ### 기여자 66 | 67 | - [@anniemon](https://github.com/anniemon) 68 | - [@iolo](https://github.com/iolo) 69 | - [@seia-soto](https://github.com/seia-soto) 70 | - [@alstjs1207](https://github.com/alstjs1207) 71 | - [@westtrain](https://github.com/westtrain) 72 | - [@6unpk](https://github.com/6unpk) 73 | -------------------------------------------------------------------------------- /docs/Guides/Benchmarking.md: -------------------------------------------------------------------------------- 1 |

Fastify

2 | 3 | ## 벤치마킹 4 | 5 | 벤치마킹은 변경 사항이 애플리케이션의 성능에 영향을 얼마나 미칠 지 측정하려고 할 때 굉장히 중요합니다. 6 | 여러분의 애플리케이션을 사용자와 기여자의 관점에서 각각 벤치마킹할 수 있도록 저희 팀은 간단한 방법을 제공하고 있습니다. 7 | 아래의 것들은 여러분이 벤치마킹을 각각의 브랜치와 Node.JS 버전별로 자동화할 수 있도록 도와줄 것입니다. 8 | 9 | 그리고 아래는 저희가 사용할 모듈들입니다: 10 | 11 | - [Autocannon](https://github.com/mcollina/autocannon): Node.JS로 작성된 HTTP/1.1 테스팅 도구. 12 | - [Branch-comparer](https://github.com/StarpTech/branch-comparer): 여러개의 깃 브랜치를 체크아웃하고 스크립트를 실행 후 결과를 로깅합니다. 13 | - [Concurrently](https://github.com/kimmobrunfeldt/concurrently): 동시에 여러 명령어를 실행시킵니다. 14 | - [Npx](https://github.com/npm/npx): npm@5.2.0와 함께 제공된, Node.JS 버전과 상관없이 로컬의 Node.JS로 스크립트를 실행시킬 수 있는 NPM 패키지 실행기입니다. 15 | 16 | ## 간단히 17 | 18 | ### 현재 브랜치에서 벤치마크 실행 19 | 20 | ```sh 21 | npm run benchmark 22 | ``` 23 | 24 | ### 다른 Node.JS 버전에서 벤치마크 실행 ✨ 25 | 26 | ```sh 27 | npx -p node@10 -- npm run benchmark 28 | ``` 29 | 30 | ## 고급스럽게 31 | 32 | ### 다른 브랜치들에서 벤치마크 실행 33 | 34 | ```sh 35 | branchcmp --rounds 2 --script "npm run benchmark" 36 | ``` 37 | 38 | ### 다른 Node.JS 버전을 사용하여 다른 브랜치들에서 벤치마크 실행 ✨ 39 | 40 | ```sh 41 | branchcmp --rounds 2 --script "npm run benchmark" 42 | ``` 43 | 44 | ### 현재 브랜치와 main 브랜치 비교하기 (Gitflow) 45 | 46 | ```sh 47 | branchcmp --rounds 2 --gitflow --script "npm run benchmark" 48 | ``` 49 | 50 | 또는 51 | 52 | ```sh 53 | npm run bench 54 | ``` 55 | 56 | ### 각각 다른 예제 사용하기 57 | 58 | 59 | 60 | ```sh 61 | branchcmp --rounds 2 -s "node ./node_modules/concurrently -k -s first \"node ./examples/asyncawait.js\" \"node ./node_modules/autocannon -c 100 -d 5 -p 10 localhost:3000/\"" 62 | ``` 63 | 64 | 65 | -------------------------------------------------------------------------------- /docs/Guides/Contributing.md: -------------------------------------------------------------------------------- 1 | # Fastify에 기여하기 2 | 3 | 4 | Fastify에 기여하는 일에 관심을 가져주셔서 감사합니다. 5 | 저희는 여러분의 지식과 지원을 받게 되어 기쁩니다. 6 | 이 가이드는 저희를 돕는 여러분을 지원하기 위한 저희의 노력입니다. 7 | 8 | > ## Note 9 | > 이것은 비공식 가이드입니다. 10 | > 공식 [기여하기 문서](https://github.com/fastify/fastify/blob/main/CONTRIBUTING.md)의 전문과 [개발자 원천 증명](https://en.wikipedia.org/wiki/Developer_Certificate_of_Origin)을 확인해주시기 바랍니다. 11 | 12 | ## 목차 13 | 14 | 15 | - [목차](#table-of-contents) 16 | - [기대하는 기여 방식](#types-of-contributions-were-looking-for) 17 | - [기본 규칙 및 기대 사항](#ground-rules--expectations) 18 | - [기여하는 방법](#how-to-contribute) 19 | - [환경 설정하기](#setting-up-your-environment) 20 | - [Visual Studio Code 사용하기](#using-visual-studio-code) 21 | 22 | ## 기대하는 기여 방식 23 | 24 | 25 | 간단하게 말하자면, 저희는 어떤 형태의 기여 방식이든 기꺼이 환영합니다. 26 | 기여하는 일에는 길고 짧음이 없습니다. 27 | 저희는 다음과 같은 기여를 기쁘게 받습니다: 28 | 29 | * 문서 개선하기: 작은 오타 수정부터 큰 단위의 문서 재작성까지 가능합니다. 30 | * 풀 리퀘스트와 [discussions](https://github.com/fastify/fastify/discussions)의 질문에 답변을 달아 다른 분들을 도울 수 있습니다. 31 | * [알려진 버그](https://github.com/fastify/fastify/issues?q=is%3Aissue+is%3Aopen+label%3Abug)를 수정할 수 있습니다. 32 | * 기존에 잘 알려지지 않은 버그를 최소한의 재현 방법과 함께 이슈에 등록해서 알릴 수 있습니다. 33 | 34 | ## 기본 규칙과 기대 사항 35 | 36 | 37 | 시작하기에 앞서서, 여러분이 숙지하길 바라는 점들을 알려드리겠습니다. 38 | 39 | * 프로젝트에서 상대방을 존중하고 배려해주세요. 40 | 이 프로젝트는 전세계의 누구에게나 열려 있고 다양한 사람들이 참여하고 있습니다. 41 | 각각의 사람마다 프로젝트를 바라보는 자신만의 시각과 견해를 가지고 있을 수 있습니다. 42 | 다른 분들의 생각을 듣고 동의하거나 절충안을 찾아서 함께 문제를 해결해 나가길 바랍니다. 43 | * 프로젝트에 참여하시려면 먼저 [작성 원칙](https://github.com/fastify/fastify/blob/main/CODE_OF_CONDUCT.md)을 확인하시고 규칙에 따라 진행하셔야 합니다. 44 | * 풀 리퀘스트를 여시려면 모든 기여가 테스트를 통과해야 한다는 점을 확실하게 해주셔야 합니다. 만약 통과하지 못한 테스트가 있다면 저희가 병합하기 전에 이유를 기술해주셔야 합니다. 45 | 46 | ## 기여하는 방법 47 | 48 | 49 | 먼저, [이슈](https://github.com/fastify/fastify/issues) 와 [풀 리퀘스트](https://github.com/fastify/fastify/pulls)를 통해서 다른 분들이 비슷한 문제나 아이디어를 제공하지는 않았는지 확인바랍니다. 50 | 51 | 만약 여러분의 생각을 찾지 못했고 가이드의 목적과도 맞아 떨어진다고 생각되시면 아래의 방법을 따라주세요. 52 | * **사소한 문제일 경우** 예를 들면, 오타 같은 문제를 수정하는 일은 수정 완료 후 풀 리퀘스트를 요청해주세요 53 | * **중요한 문제일 경우** 예를 들면, 새로운 기능을 추가하는 일과 같은 경우에는 먼저 이슈에 등록해주세요. 54 | 그래야 작업하기 전에 다른 사람들이 그 이슈에 대해 토론할 수 있습니다. 55 | 56 | 60 | 61 | ## 환경 설정하기 62 | 63 | 64 | 이 프로젝트의 코드 작성과 문서 작성 스타일을 준수해주세요. 65 | 코드와 문서를 자동으로 수정해주는 몇몇 유명한 도구들은 이 프로젝트의 스타일을 따르지 않습니다. 66 | 그러므로 이 프로젝트에서 사용하는 스타일과 [StandardJS](https://standardjs.com)의 코드 포멧팅과 일치하지 않는 이상은 사용을 지양해주시길 권장합니다. 67 | 68 | ### Visual Studio Code 사용하기 69 | 70 | 71 | 다음으로는 [Visual Studio Code (VSCode) portable](https://code.visualstudio.com/docs/editor/portable)에서 Fastify 개발 환경을 만드는 법을 찾을 수 있습니다. 72 | 이 문서는 macOS 환경을 가정하고 있지만 다른 모든 플랫폼에서도 원리는 동일합니다. 73 | 다른 플랫폼의 경우 링크로 첨부된 VSCode portable 가이드에서 확인해보세요. 74 | 75 | 먼저, [VSCode](https://code.visualstudio.com/download)를 다운로드하고 `/Applications/VSCodeFastify/` 폴더에 언팩해주세요. 76 | 그러고 나서, 터미널상에서 다음의 명령어를 실행시키고 "found"를 출력해주세요. 77 | 78 | ```sh 79 | [ -d /Applications/VSCodeFastify/Visual\ Studio\ Code.app ] && echo "found" 80 | ``` 81 | 82 | VSCode portable 가이드에 언급된 것처럼 portable 모드가 정상적으로 작동할 수 있도록 애플리케이션 샌드박스를 해제해야 합니다. 83 | 그러기 위해 터미널상에서 다음의 명령어들을 실행시켜 주세요. 84 | 85 | ```sh 86 | xattr -dr com.apple.quarantine /Applications/VSCodeFastify/Visual\ Studio\ Code.app 87 | ``` 88 | 89 | 다음으로, VSCode에서 요구하는 데이터 디렉토리를 만드세요. 90 | 91 | ```sh 92 | mkdir -p /Applications/VSCodeFastify/code-portable-data/{user-data,extensions} 93 | ``` 94 | 95 | 계속하기 전, 터미널의 `PATH`에 `code` 명령어를 추가해야 합니다. 96 | 이렇게 하기 위해 [직접 VSCode를 PATH에 추가](https://code.visualstudio.com/docs/setup/mac#_launching-from-the-command-line)할 것입니다. 97 | 위 문서에서 약술한 것처럼 절차는 여러분의 기본 쉘에 따라 다릅니다. 가이드에서 여러분이 선호하는 쉘에 맞는 절차를 따라주셔야 합니다. 98 | 그러나 `code` 도구를 직접 참조하는 대신 별칭을 정의하여 약간 조정할 것입니다. 이것은 현재 사용하고 있을 수 있는 다른 VSCode 설치와 충돌하지 않도록 하기 위한 것이며, 본 가이드를 Fastify에만 적용하기 위한 것입니다. 99 | 결론적으로는 다음의 명령어를 사용하기를 권장드립니다. 100 | 101 | ```sh 102 | alias code-fastify="/Applications/VSCodeFastify/Visual\ Studio\ Code.app/Contents/Resources/app/bin/code" 103 | ``` 104 | 105 | 정상적으로 작동했다면 `code-fastify --version` 명령어를 터미널에 넣어 다음처럼 결과를 볼 수 있습니다. 106 | 107 | ```sh 108 | ❯ code-fastify --version 109 | 1.50.0 110 | 93c2f0fbf16c5a4b10e4d5f89737d9c2c25488a3 111 | x64 112 | ``` 113 | 114 | 이제 작성하는 어떤 JavaScript라도 프로젝트 스타일에 맞도록 포맷팅해주는 확장프로그램을 설치해야 합니다. 115 | 116 | ```sh 117 | code-fastify --install-extension dbaeumer.vscode-eslint 118 | ``` 119 | 120 | 위 명령어가 성공적으로 실행되었다면, 다음 명령어를 실행하면 "found"가 출력될 겁니다. 121 | 122 | ```sh 123 | [ -d /Applications/VSCodeFastify/code-portable-data/extensions/dbaeumer.vscode-eslint-* ] && echo "found" 124 | ``` 125 | 126 | 이제 로컬에 클론한 Fastify 프로젝트의 디렉토리에서, VSCode를 열 수 있습니다: 127 | 128 | ```sh 129 | code-fastify . 130 | ``` 131 | 132 | 새 VSCode 윈도우 창이 열리면 사이드 바에서 Fastify 프로젝트 파일들을 볼 수 있습니다. 133 | 그렇다고 모든 설정이 끝난 것이 아닙니다! 134 | VSCode를 사용하기 전에 해야할 기본 설정이 몇개 남아있습니다. 135 | 136 | VSCode에서 `cmd+shift+p`를 눌러 명령어 입력 창을 열어 주세요. 137 | `open settings (json)`을 입력하고 검색된 메뉴 중 일치하는 항목을 선택하세요. 138 | 편집기를 위한 설정 파일을 열어줄 것입니다. 139 | 다음 JSON 코드를 이 문서에 붙여넣고 이미 있는 텍스트를 덮어쓴 다음 저장합니다. 140 | 141 | ```json 142 | { 143 | "[javascript]": { 144 | "editor.defaultFormatter": "dbaeumer.vscode-eslint", 145 | "editor.codeActionsOnSave": { 146 | "source.fixAll": true 147 | } 148 | }, 149 | 150 | "workbench.colorCustomizations": { 151 | "statusBar.background": "#178bb9" 152 | } 153 | } 154 | ``` 155 | 156 | 마침내, 메뉴 바를 통해 "터미널 > 새 터미널"을 선택해서 새 터미널을 에디터에 열 수 있습니다. 157 | `npm i`를 입력해 Fastify 의존성을 설치합니다. 158 | 이것으로 Fastify에 기여하기 위한 커스텀 VSCode 인스턴스의 설정을 완료했습니다. 159 | 이제 자바스크립트 파일들을 수정하고 저장할 때, 에디터가 자동으로 스타일을 수정해 줄 것입니다. 160 | -------------------------------------------------------------------------------- /docs/Guides/Database.md: -------------------------------------------------------------------------------- 1 |

Fastify

2 | 3 | ## Database 4 | 5 | Fastify 생태계는 여러 데이터베이스 엔진 연결을 위한 몇 가지 플러그인을 제공합니다. 해당 가이드에서는 Fastify에서 공식적으로 플러그인을 제공하는 데이터베이스 엔진을 다룹니다. 6 | 7 | > Fastify는 데이터베이스에 구애받지 않기에 그대로 사용하는건 얼마든지 가능합니다. 8 | > 이 가이드에 나열된 데이터베이스 플러그인의 예를 따라, 9 | > 누락된 데이터베이스 엔진을 위한 플러그인을 작성할 수 있습니다. 10 | 11 | > 만약 Fastify 플러그인을 직접 작성하고 싶다면 12 | > [plugins guide](./Plugins-Guide.md)을 참조해보세요 13 | 14 | ### [MySQL](https://github.com/fastify/fastify-mysql) 15 | 16 | 다음 명령어로 플러그인을 설치합니다 `npm i @fastify/mysql`. 17 | 18 | *예시:* 19 | 20 | ```javascript 21 | const fastify = require('fastify')() 22 | 23 | fastify.register(require('@fastify/mysql'), { 24 | connectionString: 'mysql://root@localhost/mysql' 25 | }) 26 | 27 | fastify.get('/user/:id', function(req, reply) { 28 | fastify.mysql.query( 29 | 'SELECT id, username, hash, salt FROM users WHERE id=?', [req.params.id], 30 | function onResult (err, result) { 31 | reply.send(err || result) 32 | } 33 | ) 34 | }) 35 | 36 | fastify.listen({ port: 3000 }, err => { 37 | if (err) throw err 38 | console.log(`server listening on ${fastify.server.address().port}`) 39 | }) 40 | ``` 41 | 42 | ### [Postgres](https://github.com/fastify/fastify-postgres) 43 | 다음 명령어로 플러그인을 설치합니다 `npm i pg @fastify/postgres`. 44 | 45 | *예시*: 46 | 47 | ```javascript 48 | const fastify = require('fastify')() 49 | 50 | fastify.register(require('@fastify/postgres'), { 51 | connectionString: 'postgres://postgres@localhost/postgres' 52 | }) 53 | 54 | fastify.get('/user/:id', function (req, reply) { 55 | fastify.pg.query( 56 | 'SELECT id, username, hash, salt FROM users WHERE id=$1', [req.params.id], 57 | function onResult (err, result) { 58 | reply.send(err || result) 59 | } 60 | ) 61 | }) 62 | 63 | fastify.listen({ port: 3000 }, err => { 64 | if (err) throw err 65 | console.log(`server listening on ${fastify.server.address().port}`) 66 | }) 67 | ``` 68 | 69 | ### [Redis](https://github.com/fastify/fastify-redis) 70 | 다음 명령어로 플러그인을 설치합니다 `npm i @fastify/redis` 71 | 72 | *예시:* 73 | 74 | ```javascript 75 | 'use strict' 76 | 77 | const fastify = require('fastify')() 78 | 79 | fastify.register(require('@fastify/redis'), { host: '127.0.0.1' }) 80 | // 또는 81 | fastify.register(require('@fastify/redis'), { url: 'redis://127.0.0.1', /* other redis options */ }) 82 | 83 | fastify.get('/foo', function (req, reply) { 84 | const { redis } = fastify 85 | redis.get(req.query.key, (err, val) => { 86 | reply.send(err || val) 87 | }) 88 | }) 89 | 90 | fastify.post('/foo', function (req, reply) { 91 | const { redis } = fastify 92 | redis.set(req.body.key, req.body.value, (err) => { 93 | reply.send(err || { status: 'ok' }) 94 | }) 95 | }) 96 | 97 | fastify.listen({ port: 3000 }, err => { 98 | if (err) throw err 99 | console.log(`server listening on ${fastify.server.address().port}`) 100 | }) 101 | ``` 102 | 103 | 기본적으로 `@fastify/redis`는 Fastify 서버가 닫혀도 자동으로 클라이언트 연결을 닫지 않습니다. 104 | 따라서 자동으로 닫히게 반영하기 위해서는 다음과 같이 클라이언트를 등록해야합니다. 105 | 106 | ```javascript 107 | fastify.register(require('@fastify/redis'), { 108 | client: redis, 109 | closeClient: true 110 | }) 111 | ``` 112 | 113 | ### [Mongo](https://github.com/fastify/fastify-mongodb) 114 | 다음 명령어로 플러그인을 설치합니다 `npm i @fastify/mongodb` 115 | 116 | *예시:* 117 | ```javascript 118 | const fastify = require('fastify')() 119 | 120 | fastify.register(require('@fastify/mongodb'), { 121 | // force to close the mongodb connection when app stopped 122 | // the default value is false 123 | forceClose: true, 124 | 125 | url: 'mongodb://mongo/mydb' 126 | }) 127 | 128 | fastify.get('/user/:id', function (req, reply) { 129 | // 또는 this.mongo.client.db('mydb').collection('users') 130 | const users = this.mongo.db.collection('users') 131 | 132 | // id가 ObjectId 형식인 경우 새로운 ObjectId를 생성해야합니다. 133 | const id = this.mongo.ObjectId(req.params.id) 134 | users.findOne({ id }, (err, user) => { 135 | if (err) { 136 | reply.send(err) 137 | return 138 | } 139 | reply.send(user) 140 | }) 141 | }) 142 | 143 | fastify.listen({ port: 3000 }, err => { 144 | if (err) throw err 145 | }) 146 | ``` 147 | 148 | ### [LevelDB](https://github.com/fastify/fastify-leveldb) 149 | 다음 명령어로 플러그인을 설치합니다 `npm i @fastify/leveldb` 150 | 151 | *예시:* 152 | ```javascript 153 | const fastify = require('fastify')() 154 | 155 | fastify.register( 156 | require('@fastify/leveldb'), 157 | { name: 'db' } 158 | ) 159 | 160 | fastify.get('/foo', async function (req, reply) { 161 | const val = await this.level.db.get(req.query.key) 162 | return val 163 | }) 164 | 165 | fastify.post('/foo', async function (req, reply) { 166 | await this.level.db.put(req.body.key, req.body.value) 167 | return { status: 'ok' } 168 | }) 169 | 170 | fastify.listen({ port: 3000 }, err => { 171 | if (err) throw err 172 | console.log(`server listening on ${fastify.server.address().port}`) 173 | }) 174 | ``` 175 | 176 | ### 데이터베이스 라이브러리 플러그인 만들기 177 | 이외에도 다른 데이터베이스 라이브러리용 플러그인을 작성할 수 있습니다. (예: Knex, Prisma, or TypeORM) 178 | 이번 예시에서는 [Knex](https://knexjs.org/)를 사용해보겠습니다. 179 | 180 | ```javascript 181 | 'use strict' 182 | 183 | const fp = require('fastify-plugin') 184 | const knex = require('knex') 185 | 186 | function knexPlugin(fastify, options, done) { 187 | if(!fastify.knex) { 188 | const knex = knex(options) 189 | fastify.decorate('knex', knex) 190 | 191 | fastify.addHook('onClose', (fastify, done) => { 192 | if (fastify.knex === knex) { 193 | fastify.knex.destroy(done) 194 | } 195 | }) 196 | } 197 | 198 | done() 199 | } 200 | 201 | export default fp(knexPlugin, { name: 'fastify-knex-example' }) 202 | ``` 203 | 204 | ### 데이터베이스 엔진 플러그인 만들기 205 | 206 | 이번 예제에서는 기본적인 MySQL 플러그인을 처음부터 만들어봅니다. (이는 207 | 이해를 돕기 위한 예제이므로, 프로덕션 환경에서는 공식 플러그인을 사용해주세요.) 208 | 209 | ```javascript 210 | const fp = require('fastify-plugin') 211 | const mysql = require('mysql2/promise') 212 | 213 | function fastifyMysql(fastify, options, done) { 214 | const connection = mysql.createConnection(options) 215 | 216 | if (!fastify.mysql) { 217 | fastify.decorate('mysql', connection) 218 | } 219 | 220 | fastify.addHook('onClose', (fastify, done) => connection.end().then(done).catch(done)) 221 | 222 | done() 223 | } 224 | 225 | export default fp(fastifyMysql, { name: 'fastify-mysql-example' }) 226 | ``` 227 | 228 | ### 마이그레이션 229 | 230 | 데이터베이스 스키마 마이그레이션은 데이터베이스 관리 및 개발의 필수입니다. 231 | 마이그레이션은 반복적이고 테스트 가능한 방식으로 데이터베이스 스키마를 관리하고 데이터 손실을 방지하는 방법을 제공합니다. 232 | 233 | 본 가이드의 시작부분에서 설명했듯이, Fastify는 데이터베이스 형식에 얽매이지 않으며 어떤 NodeJS 데이터베이스 마이그레이션 툴이든 적용 가능합니다. 234 | Postgres, MySQL, SQLServer 그리고 SQLite를 지원하는 [Postgrator](https://www.npmjs.com/package/postgrator)의 예시를 따라해봅니다. 235 | MongoDB 마이그레이션의 경우 [migrate-mongo](https://www.npmjs.com/package/migrate-mongo)를 참조하세요. 236 | 237 | #### [Postgrator](https://www.npmjs.com/package/postgrator) 238 | 239 | Postgrator는 SQL 스크립트 디렉토리로 데이터베이스 스키마를 변경하는 NodeJS SQL 마이그레이션 도구입니다. 240 | 마이그레이션 폴더내에 각 SQL 파일명은 다음과 같은 패턴으로 작성되어야 합니다: : ` [version].[action].[optional-description].sql`. 241 | 242 | **version:** 순차적으로 증가하는 양수 값이어야 합니다. (e.g. `001` 혹은 타입스탬프값). 243 | 244 | **action:** 반드시 `do` 혹은 `undo`여야 합니다. `do` 는 버전을 올리며, `undo` 245 | 는 반대로 작업을 되돌리는 것을 의미합니다. 통상적으로 타 마이그레이션 도구에서 사용되는 `up` 및 `down` 과 같은 의미입니다. 246 | 247 | **optional-description** 마이그레이션에 따른 변경사항을 기술합니다. 248 | 선택 사항이지만, 해당 마이그레이션의 변경 사항이 어떤 것인지 쉽게 알아볼 수 있도록 사용해야 합니다. 249 | 250 | 이번 예시에서는 'users' 테이블을 생성하는 단일 마이그레이션 파일을 만들고, 'Postgrator'를 사용해 마이그레이션을 실행합니다. 251 | 252 | > `npm i pg postgrator`를 실행하여 예제 구동에 필요한 의존성을 설치합니다. 253 | 254 | ```sql 255 | // 001.do.create-users-table.sql 256 | CREATE TABLE IF NOT EXISTS users ( 257 | id SERIAL PRIMARY KEY NOT NULL, 258 | created_at DATE NOT NULL DEFAULT CURRENT_DATE, 259 | firstName TEXT NOT NULL, 260 | lastName TEXT NOT NULL 261 | ); 262 | ``` 263 | ```javascript 264 | const pg = require('pg') 265 | const Postgrator = require('postgrator') 266 | const path = require('path') 267 | 268 | async function migrate() { 269 | const client = new pg.Client({ 270 | host: 'localhost', 271 | port: 5432, 272 | database: 'example', 273 | user: 'example', 274 | password: 'example', 275 | }); 276 | 277 | try { 278 | const postgrator = new Postgrator({ 279 | migrationPattern: path.join(__dirname, '/migrations/*'), 280 | driver: 'pg', 281 | database: 'example', 282 | schemaTable: 'migrations', 283 | currentSchema: 'public', // Postgres and MS SQL Server 만 적용됩니다. 284 | execQuery: (query) => client.query(query), 285 | }); 286 | 287 | const result = await postgrator.migrate() 288 | 289 | if (result.length === 0) { 290 | console.log( 291 | 'No migrations run for schema "public". Already at the latest one.' 292 | ) 293 | } 294 | 295 | console.log('Migration done.') 296 | 297 | process.exitCode = 0 298 | } catch(err) { 299 | console.error(error) 300 | process.exitCode = 1 301 | } 302 | 303 | await client.end() 304 | } 305 | 306 | migrate() 307 | ``` 308 | -------------------------------------------------------------------------------- /docs/Guides/Fluent-Schema.md: -------------------------------------------------------------------------------- 1 |

Fastify

2 | 3 | ## Fluent Schema 4 | 5 | [유효성 검사 및 직렬화](../Reference/Validation-and-Serialization.md) 문서는 6 | JSON 스키마 유효성 검사를 설정하기 위해 Fastify에서 허용하는 모든 매개변수를 설명합니다. 7 | JSON 스키마를 사용해 입력의 유효성을 검사하고 직렬화를 통해 출력을 최적화합니다. 8 | 9 | [`fluent-json-schema`](https://github.com/fastify/fluent-json-schema) 를 10 | 사용하여 상수의 재사용을 허용하면서 이 작업을 단순화할 수 있습니다. 11 | 12 | ### 기본 설정 13 | 14 | ```js 15 | const S = require('fluent-json-schema') 16 | 17 | // 아래와 같은 개체를 사용하거나 DB를 쿼리하여 값을 가져올 수 있습니다. 18 | const MY_KEYS = { 19 | KEY1: 'ONE', 20 | KEY2: 'TWO' 21 | } 22 | 23 | const bodyJsonSchema = S.object() 24 | .prop('someKey', S.string()) 25 | .prop('someOtherKey', S.number()) 26 | .prop('requiredKey', S.array().maxItems(3).items(S.integer()).required()) 27 | .prop('nullableKey', S.mixed([S.TYPES.NUMBER, S.TYPES.NULL])) 28 | .prop('multipleTypesKey', S.mixed([S.TYPES.BOOLEAN, S.TYPES.NUMBER])) 29 | .prop('multipleRestrictedTypesKey', S.oneOf([S.string().maxLength(5), S.number().minimum(10)])) 30 | .prop('enumKey', S.enum(Object.values(MY_KEYS))) 31 | .prop('notTypeKey', S.not(S.array())) 32 | 33 | const queryStringJsonSchema = S.object() 34 | .prop('name', S.string()) 35 | .prop('excitement', S.integer()) 36 | 37 | const paramsJsonSchema = S.object() 38 | .prop('par1', S.string()) 39 | .prop('par2', S.integer()) 40 | 41 | const headersJsonSchema = S.object() 42 | .prop('x-foo', S.string().required()) 43 | 44 | // `.valueOf()`는 호출할 필요가 없다는 것에 주의하세요! 45 | const schema = { 46 | body: bodyJsonSchema, 47 | querystring: queryStringJsonSchema, // (or) query: queryStringJsonSchema 48 | params: paramsJsonSchema, 49 | headers: headersJsonSchema 50 | } 51 | 52 | fastify.post('/the/url', { schema }, handler) 53 | ``` 54 | 55 | ### 재사용 56 | 57 | `fluent-json-schema`를 사용하면 스키마를 보다 쉽고 프로그래밍적으로 다루고 58 | `add Schema()` 메서드로 재사용할 수 있습니다. [유효성 검사 및 직렬화](../Reference/Validation-and-Serialization.md#adding-a-shared-schema) 59 | 문서에 자세히 설명되어 있는 두 가지 다른 방법으로 스키마를 참조할 수 있습니다. 60 | 61 | 몇 가지 사용 예는 다음과 같습니다: 62 | 63 | **`$ref-way`**: 외부 스키마를 참조합니다. 64 | 65 | ```js 66 | const addressSchema = S.object() 67 | .id('#address') 68 | .prop('line1').required() 69 | .prop('line2') 70 | .prop('country').required() 71 | .prop('city').required() 72 | .prop('zipcode').required() 73 | 74 | const commonSchemas = S.object() 75 | .id('https://fastify/demo') 76 | .definition('addressSchema', addressSchema) 77 | .definition('otherSchema', otherSchema) // 필요한 스키마를 추가할 수 있습니다. 78 | 79 | fastify.addSchema(commonSchemas) 80 | 81 | const bodyJsonSchema = S.object() 82 | .prop('residence', S.ref('https://fastify/demo#address')).required() 83 | .prop('office', S.ref('https://fastify/demo#/definitions/addressSchema')).required() 84 | 85 | const schema = { body: bodyJsonSchema } 86 | 87 | fastify.post('/the/url', { schema }, handler) 88 | ``` 89 | 90 | 91 | **`replace-way`**: 유효성 검사 프로세스 전에 대체할 공유 스키마를 참조하십시오. 92 | 93 | ```js 94 | const sharedAddressSchema = { 95 | $id: 'sharedAddress', 96 | type: 'object', 97 | required: ['line1', 'country', 'city', 'zipcode'], 98 | properties: { 99 | line1: { type: 'string' }, 100 | line2: { type: 'string' }, 101 | country: { type: 'string' }, 102 | city: { type: 'string' }, 103 | zipcode: { type: 'string' } 104 | } 105 | } 106 | fastify.addSchema(sharedAddressSchema) 107 | 108 | const bodyJsonSchema = { 109 | type: 'object', 110 | properties: { 111 | vacation: 'sharedAddress#' 112 | } 113 | } 114 | 115 | const schema = { body: bodyJsonSchema } 116 | 117 | fastify.post('/the/url', { schema }, handler) 118 | ``` 119 | 120 | `fastify.addSchema`를 사용할 때 `$ref-way`와 `replace-way`를 같이 사용할 수 있습니다. 121 | -------------------------------------------------------------------------------- /docs/Guides/Getting-Started.md: -------------------------------------------------------------------------------- 1 |

Fastify

2 | 3 | ## 시작하기 4 | 5 | 안녕하세요! Fastify를 찾아주셔서 감사합니다! 6 | 7 | 본 문서는 Fastify 프레임워크의 특징을 쉽고 간단히 알아볼 수 있도록 하는 데 초점을 맞췄습니다. 본 문서는 예제가 포함된 쉽고 간단한 소개문이며 동시에 다른 문서들의 진입점 역할을 합니다. 8 | 9 | 그러면 시작해봅시다! 10 | 11 | ### 설치 12 | 13 | 14 | npm으로 설치하기: 15 | ``` 16 | npm i fastify 17 | ``` 18 | yarn으로 설치하기: 19 | ``` 20 | yarn add fastify 21 | ``` 22 | 23 | ### 첫번째 서버 24 | 25 | 26 | 다음과 같이 첫 번째 서버를 위한 코드를 작성해봅시다. 27 | ```js 28 | // 프레임워크를 가져와 인스턴스화 합니다 29 | 30 | // ESM 31 | import Fastify from 'fastify' 32 | const fastify = Fastify({ 33 | logger: true 34 | }) 35 | // CommonJs 36 | const fastify = require('fastify')({ 37 | logger: true 38 | }) 39 | 40 | // 라우트를 정의합니다. 41 | fastify.get('/', function (request, reply) { 42 | reply.send({ hello: 'world' }) 43 | }) 44 | 45 | // 서버를 실행합니다! 46 | fastify.listen({ port: 3000 }, function (err, address) { 47 | if (err) { 48 | fastify.log.error(err) 49 | process.exit(1) 50 | } 51 | // 서버가 현재 ${address}에서 접속을 대기중입니다. 52 | }) 53 | ``` 54 | 55 | `async/await` 사용을 더 선호하시나요? Fastify는 당연하게도 이를 지원합니다. 56 | 57 | ```js 58 | // ESM 59 | import Fastify from 'fastify' 60 | const fastify = Fastify({ 61 | logger: true 62 | }) 63 | // CommonJs 64 | const fastify = require('fastify')({ 65 | logger: true 66 | }) 67 | 68 | fastify.get('/', async (request, reply) => { 69 | return { hello: 'world' } 70 | }) 71 | 72 | /** 73 | * 서버를 실행합니다! 74 | */ 75 | const start = async () => { 76 | try { 77 | await fastify.listen({ port: 3000 }) 78 | } catch (err) { 79 | fastify.log.error(err) 80 | process.exit(1) 81 | } 82 | } 83 | start() 84 | ``` 85 | 86 | 훌륭합니다! 쉽게 구현했습니다. 87 | 88 | 그러나 안타깝게도, 이보다 복잡한 어플리케이션을 만들려면 훨씬 많은 코드를 작성해야 합니다. 89 | 새로운 어플리케이션을 구현해야 할 때 부딪히는 고전적인 문제로는 많은 파일을 어떻게 다룰지, 비동기적인 부트스트래핑을 어떻게 처리할지, 코드의 아키텍처는 어떻게 구성할지와 같은 것들입니다. 90 | 91 | Fastify는 이와 같은 문제 이외에도 더 많은 문제를 쉽게 해결할 수 있는 플랫폼을 제공합니다! 92 | 93 | > ## 주의 94 | > 위의 예와 뒤따르는 이 글의 다른 예시들에서도, 로컬호스트인 `127.0.0.1` 만 접속을 허용하도록 기본 설정이 되어 있습니다. 95 | > 모든 IPv4 주소로부터 접속을 허용하도록 하기 위해서는 다음과 같이 `0.0.0.0`로 변경해야합니다: 96 | > 97 | > ```js 98 | > fastify.listen({ port: 3000, host: '0.0.0.0' }, function (err, address) { 99 | > if (err) { 100 | > fastify.log.error(err) 101 | > process.exit(1) 102 | > } 103 | > fastify.log.info(`server listening on ${address}`) 104 | > }) 105 | > ``` 106 | > 107 | > 유사하게, `::1` 는 IPv6 로컬 접속만을 허용합니다. 다음과 같이 108 | > 운영체제에서 IPv6를 지원하는 경우 `::`는 모든 IPv6 주소의 접속을 받아들일 수 있으며, IPv4 주소 연결 역시 수락합니다. 109 | > 110 | > 도커(Docker)를 사용해 배포하는 경우 (혹은 이외의 방식이더라도) `0.0.0.0` 또는 111 | > `::`를 사용하는 것이 애플리케이션을 쉽게 외부로 공개할 수 있는 방법입니다. 112 | 113 | ### 첫번째 플러그인 114 | 115 | 116 | 자바스크립트에서 모든 것이 객체이듯이, Fastify에서는 모든 것들이 Plugin입니다. 117 | 118 | 본격적으로 들어가기 전에 한 번 어떻게 작동하는지 확인해봅시다. 119 | 120 | 121 | 먼저 기본 서버를 구성해봅시다, 서버 진입점에 라우트를 정의하는 대신에 외부 파일로 정의할 것입니다.([라우트 정의](../Reference/Routes.md) 문서에 대해 확인해보세요). 122 | ```js 123 | // ESM 124 | import Fastify from 'fastify' 125 | import firstRoute from './our-first-route' 126 | /** 127 | * @type {import('fastify').FastifyInstance} Fastify 인스턴스 128 | */ 129 | const fastify = Fastify({ 130 | logger: true 131 | }) 132 | 133 | fastify.register(firstRoute) 134 | 135 | fastify.listen({ port: 3000 }, function (err, address) { 136 | if (err) { 137 | fastify.log.error(err) 138 | process.exit(1) 139 | } 140 | // 서버가 현재 ${address}에서 접속을 대기중입니다. 141 | }) 142 | ``` 143 | 144 | ```js 145 | // CommonJs 146 | /** 147 | * @type {import('fastify').FastifyInstance} Fastify 인스턴스 148 | */ 149 | const fastify = require('fastify')({ 150 | logger: true 151 | }) 152 | 153 | fastify.register(require('./our-first-route')) 154 | 155 | fastify.listen({ port: 3000 }, function (err, address) { 156 | if (err) { 157 | fastify.log.error(err) 158 | process.exit(1) 159 | } 160 | // 서버가 현재 ${address}에서 접속을 대기중입니다. 161 | }) 162 | ``` 163 | 164 | ```js 165 | // our-first-route.js 166 | 167 | /** 168 | * 라우트를 캡슐화 합니다. 169 | * @param {FastifyInstance} fastify 캡슐화된 Fastify 인스턴스 170 | * @param {Object} options 플러그인 옵션, https://www.fastify.io/docs/latest/Reference/Plugins/#plugin-options 를 참조하세요 171 | */ 172 | async function routes (fastify, options) { 173 | fastify.get('/', async (request, reply) => { 174 | return { hello: 'world' } 175 | }) 176 | } 177 | 178 | module.exports = routes 179 | ``` 180 | 이번 예제에서는, Fastify 프레임워크의 핵심이 되는 `register` API를 사용했습니다. 이는 라우트(routes)나 플러그인(plugins) 기타 등등을 추가하기 위한 유일한 방법입니다. 181 | 182 | 해당 가이드의 처음 부분에서, Fastify가 비동기적 방식의 부트스트래핑(bootstrapping)의 기반을 제공한다고 이야기했습니다. 183 | 이것이 왜 중요할까요? 184 | 185 | 데이터 저장을 다루기 위해 데이터베이스 연결이 필요한 시나리오를 고려해봅시다. 서버가 연결되기 전에 데이터베이스 연결을 사용할 수 있어야 합니다. 186 | 어떻게 이러한 문제를 풀어나갈 수 있을까요? 187 | 188 | 고전적인 해결책으로는 복잡한 콜백함수 또는 프로미스를 사용하는 것입니다. - 이 경우 프레임워크 API 와 다른 라이브러리 및 어플리케이션 코드를 혼합하는 시스템이 됩니다. 189 | 190 | Fastify는 이러한 것들을 내부적으로 다루고 있으며, 최소한의 노력으로 실현할 수 있게 합니다! 191 | 192 | 그러면 위의 예제를 데이터베이스 연결로 재작성해봅시다. 193 | 194 | 첫 번쨰, `fastify-plugin` 와 `@fastify/mongodb`를 설치합니다: 195 | 196 | ``` 197 | npm i fastify-plugin @fastify/mongodb 198 | ``` 199 | 200 | **server.js** 201 | ```js 202 | // ESM 203 | import Fastify from 'fastify' 204 | import dbConnector from './our-db-connector' 205 | import firstRoute from './our-first-route' 206 | 207 | /** 208 | * @type {import('fastify').FastifyInstance} Instance of Fastify 209 | */ 210 | const fastify = Fastify({ 211 | logger: true 212 | }) 213 | fastify.register(dbConnector) 214 | fastify.register(firstRoute) 215 | 216 | fastify.listen({ port: 3000 }, function (err, address) { 217 | if (err) { 218 | fastify.log.error(err) 219 | process.exit(1) 220 | } 221 | // 서버가 현재 ${address}에서 접속을 대기중입니다. 222 | }) 223 | ``` 224 | 225 | ```js 226 | // CommonJs 227 | /** 228 | * @type {import('fastify').FastifyInstance} Fastify 인스턴스 229 | */ 230 | const fastify = require('fastify')({ 231 | logger: true 232 | }) 233 | 234 | fastify.register(require('./our-db-connector')) 235 | fastify.register(require('./our-first-route')) 236 | 237 | fastify.listen({ port: 3000 }, function (err, address) { 238 | if (err) { 239 | fastify.log.error(err) 240 | process.exit(1) 241 | } 242 | // 서버가 현재 ${address}에서 접속을 대기중입니다. 243 | }) 244 | 245 | ``` 246 | 247 | **our-db-connector.js** 248 | ```js 249 | // ESM 250 | import fastifyPlugin from 'fastify-plugin' 251 | import fastifyMongo from '@fastify/mongodb' 252 | 253 | /** 254 | * @param {FastifyInstance} fastify 255 | * @param {Object} options 256 | */ 257 | async function dbConnector (fastify, options) { 258 | fastify.register(fastifyMongo, { 259 | url: 'mongodb://localhost:27017/test_database' 260 | }) 261 | } 262 | 263 | // fastify 플러그인으로 플러그인 함수를 감싸면, 플러그인 내부의 상위 스코프에 선언된 데코레이터와 훅을 노출합니다. 264 | module.exports = fastifyPlugin(dbConnector) 265 | 266 | ``` 267 | 268 | ```js 269 | // CommonJs 270 | /** 271 | * @type {import('fastify-plugin').FastifyPlugin} 272 | */ 273 | const fastifyPlugin = require('fastify-plugin') 274 | 275 | 276 | /** 277 | * MongoDB 데이터베이스에 연결합니다. 278 | * @param {FastifyInstance} fastify 캡슐화된 Fastify 인스턴스 279 | * @param {Object} options 플러그인 옵션, https://www.fastify.io/docs/latest/Reference/Plugins/#plugin-options 를 참조하세요 280 | */ 281 | async function dbConnector (fastify, options) { 282 | fastify.register(require('@fastify/mongodb'), { 283 | url: 'mongodb://localhost:27017/test_database' 284 | }) 285 | } 286 | 287 | // fastify 플러그인으로 플러그인 함수를 감싸면, 플러그인 내부의 상위 스코프에 선언된 데코레이터와 훅을 노출합니다. 288 | module.exports = fastifyPlugin(dbConnector) 289 | 290 | ``` 291 | 292 | **our-first-route.js** 293 | ```js 294 | /** 295 | * 캡슐화된 라우트를 제공하는 플러그인 296 | * @param {FastifyInstance} fastify 캡슐화된 Fastify 인스턴스 297 | * @param {Object} options 플러그인 옵션, https://www.fastify.io/docs/latest/Reference/Plugins/#plugin-options 를 참조하세요 298 | */ 299 | async function routes (fastify, options) { 300 | const collection = fastify.mongo.db.collection('test_collection') 301 | 302 | fastify.get('/', async (request, reply) => { 303 | return { hello: 'world' } 304 | }) 305 | 306 | fastify.get('/animals', async (request, reply) => { 307 | const result = await collection.find().toArray() 308 | if (result.length === 0) { 309 | throw new Error('No documents found') 310 | } 311 | return result 312 | }) 313 | 314 | fastify.get('/animals/:animal', async (request, reply) => { 315 | const result = await collection.findOne({ animal: request.params.animal }) 316 | if (!result) { 317 | throw new Error('Invalid value') 318 | } 319 | return result 320 | }) 321 | 322 | const animalBodyJsonSchema = { 323 | type: 'object', 324 | required: ['animal'], 325 | properties: { 326 | animal: { type: 'string' }, 327 | }, 328 | } 329 | 330 | const schema = { 331 | body: animalBodyJsonSchema, 332 | } 333 | 334 | fastify.post('/animals', { schema }, async (request, reply) => { 335 | // we can use the `request.body` object to get the data sent by the client 336 | const result = await collection.insertOne({ animal: request.body.animal }) 337 | return result 338 | }) 339 | } 340 | 341 | module.exports = routes 342 | ``` 343 | 344 | 세상에, 정말 빠르네요! 345 | 346 | 지금까지 새로운 개념을 소개하면서 우리가 무엇을 했었는지 한 번 되돌아봅시다. 347 | 348 | 알다시피, `register` 를 데이터베이스 커넥터와 라우팅 등록에 사용했습니다. 349 | 350 | 이것은 Fastify의 최고 기능 중 하나입니다. 정의하는 순서대로 플러그인을 가져올 것이며, 351 | 하나의 플러그인을 가져오면 그다음 플러그인을 가져옵니다. 이런 방식으로 우리는 첫 번째 플러그인에 데이터베이스 커넥터를 등록하고 두 번째 플러그인에서 사용할 수 있습니다. *(플러그인의 스코프를 다루는 법을 이해하기 위해서는 [이곳을](../Reference/Plugins.md#handle-the-scope))을 읽어보세요) 352 | 353 | 플러그인의 불러오기는 `fastify.listen()`, `fastify.inject()` 또는 `fastify.ready()`를 사용할 때 시작합니다. 354 | 355 | MongoDB 플러그인은 `decorate` API를 사용해 전역적으로 사용가능한 Fastify 인스턴스에 커스텀 객체를 추가합니다. 356 | 해당 API의 사용은 쉬운 코드 재사용과 코드 감소 그리고 로직의 중복 제거를 쉽게 도와줍니다. 357 | 358 | Fastify의 플러그인이 어떻게 작동하는지, 새로운 플러그인은 어떻게 개발하는지, 어플리케이션을 비동기적으로 부트스래핑하는 복잡함을 해결하기 위한 Fastify의 모든 API 세부 사항을 359 | 더 자세히 알기 위해서는 [플러그인을 여행하는 히치하이커를 위한 안내서](./Plugins-Guide.md) 를 읽어보세요. 360 | 361 | ### 플러그인 순서대로 불러오기 362 | 363 | 364 | 어플리케이션의 일관적이고 예측할 수 있는 행동을 보장하기 위해, 다음과 같이 코드를 순차적으로 불러오는 걸 강력히 권장합니다: 365 | ``` 366 | └── plugins (from the Fastify ecosystem) 367 | └── your plugins (your custom plugins) 368 | └── decorators 369 | └── hooks 370 | └── your services 371 | ``` 372 | 이러한 방식으로, 현재 스코프에서 정의된 모든 속성에 대해 언제든지 접근할 수 있습니다. 373 | 374 | 이전에 논의했듯이, Fastify는 견고한 캡슐화 모델을 제공하며, 독립적인 하나의 서비스로써 어플리케이션을 구성하도록 도와줍니다. 375 | 만약 플러그인을 라우트의 하위 집합만으로 등록하기를 원한다면, 위의 구조를 그대로 복제하면 그만입니다. 376 | ``` 377 | └── plugins (from the Fastify ecosystem) 378 | └── your plugins (your custom plugins) 379 | └── decorators 380 | └── hooks 381 | └── your services 382 | │ 383 | └── service A 384 | │ └── plugins (from the Fastify ecosystem) 385 | │ └── your plugins (your custom plugins) 386 | │ └── decorators 387 | │ └── hooks 388 | │ └── your services 389 | │ 390 | └── service B 391 | └── plugins (from the Fastify ecosystem) 392 | └── your plugins (your custom plugins) 393 | └── decorators 394 | └── hooks 395 | └── your services 396 | ``` 397 | 398 | ### 데이터 검증하기 399 | 400 | 401 | 데이터의 검증은 아주 중요하며 프레임워크의 핵심 개념이기도 합니다. 402 | 403 | 들어오는 요청에 대해 검증하기 위해 Fastify는 [JSON 스키마](https://json-schema.org/)를 사용합니다. 404 | (JTD 스키마도 약하게(loosely) 지원합니다, 그러나 `jsonShorthand` 옵션은 반드시 비활성화되어야 합니다) 405 | 406 | 라우트에서 데이터 검증에 대해 확인해보기 위해 예제를 한 번 살펴봅시다: 407 | ```js 408 | /** 409 | * @type {import('fastify').RouteShorthandOptions} 410 | * @const 411 | */ 412 | const opts = { 413 | schema: { 414 | body: { 415 | type: 'object', 416 | properties: { 417 | someKey: { type: 'string' }, 418 | someOtherKey: { type: 'number' } 419 | } 420 | } 421 | } 422 | } 423 | 424 | fastify.post('/', opts, async (request, reply) => { 425 | return { hello: 'world' } 426 | }) 427 | ``` 428 | 이번 예제에서는 어떻게 라우트로 옵션 객체를 던져줄 수 있는지 보여줍니다. 라우트와 관련한 모든 스키마(`body`, `querystring`, 429 | `params`, 그리고 `headers`)를 포함하는 `schema` 키를 받을 수 있습니다. 430 | 431 | 더 많은 것을 배우려면 [검증과 직렬화](../Reference/Validation-and-Serialization.md)를 읽어보세요. 432 | 433 | ### 데이터 직렬화 434 | 435 | 436 | Fastify는 JSON을 최고 수준으로 지원합니다. 그리고 JSON 본문을 파싱하고 JSON 출력을 직렬화하는 데 있어 최적화가 아주 잘 되어있습니다. 437 | 438 | JSON 직렬화의 속도를 높이려면 (그렇습니다. 직렬화는 느립니다!) 다음 예제에서 보이는 것처럼 스키마 옵션의 `response` 키를 사용하세요: 439 | ```js 440 | /** 441 | * @type {import('fastify').RouteShorthandOptions} 442 | * @const 443 | */ 444 | const opts = { 445 | schema: { 446 | response: { 447 | 200: { 448 | type: 'object', 449 | properties: { 450 | hello: { type: 'string' } 451 | } 452 | } 453 | } 454 | } 455 | } 456 | 457 | fastify.get('/', opts, async (request, reply) => { 458 | return { hello: 'world' } 459 | }) 460 | ``` 461 | 보이는 것과 같이 응답 스키마를 명시하면, 직렬화 속도를 2-3 배 정도 빠르게 할 수 있습니다. 462 | 또한 잠재적인 중요 데이터의 누락으로부터 도와주기도 합니다. Fastify는 현재 응답 스키마에 보이는 데이터에 대해서만 직렬화를 하기 때문입니다. 463 | 관련해서 자세한 내용은 [검증과 직렬화](../Reference/Validation-and-Serialization.md)를 참조하세요. 464 | 465 | ### 요청 페이로드(payload) 파싱 466 | 467 | 468 | Fastify는 `'application/json'`과 `'text/plain'` 요청 페이로드를 기본적으로 파싱하며, 469 | [Fastify요청](../Reference/Request.md) 객체의 `request.body` 에서 파싱된 결과를 접근할 수 있습니다. 470 | 471 | 다음 예제에서는 파싱된 요청의 본문을 클라이언트에 되돌려줍니다: 472 | 473 | ```js 474 | /** 475 | * @type {import('fastify').RouteShorthandOptions} 476 | */ 477 | const opts = {} 478 | fastify.post('/', opts, async (request, reply) => { 479 | return request.body 480 | }) 481 | ``` 482 | 483 | Fastif의 기본 파싱 기능과 다른 콘텐츠 타입을 지원하려면 어떻게 해야하는지 자세히 알아보려면 [Content-Type Parser](../Reference/ContentTypeParser.md) 을 읽어보세요. 484 | 485 | ### 서버 확장하기 486 | 487 | 488 | Fastify는 최대한 확장 가능하면서도 최소한의 기능을 제공하기 위해 만들어졌습니다. 489 | 우리는 기본 뼈대만 있는 프레임워크가 좋은 어플리케이션을 만들기 위한 필수 요소의 모든 것이라 믿습니다. 490 | 491 | 달리 말하면, Fastify는 "배터리가 내장된" 프레임워크가 아니며, 훌륭한 [생태계](./Ecosystem.md)가 이를 뒷받침하고 있습니다. 492 | 493 | ### 서버 테스트하기 494 | 495 | 496 | Fastify는 테스팅 프레임워크를 제공하지 않고 있지만, 우리는 여러분이 Fastify의 기능과 아키텍처를 이용하는 테스트를 작성하는 방법을 추천합니다. 497 | 498 | 자세한 내용은 [테스팅](./Testing.md) 문서를 읽어보세요! 499 | 500 | ### CLI 에서 서버 실행하기 501 | 502 | 503 | Fastify [fastify-cli](https://github.com/fastify/fastify-cli) 덕분에 CLI 통합도 가능합니다. 504 | 505 | 먼저, `fastify-cli`를 설치합니다: 506 | 507 | ``` 508 | npm i fastify-cli 509 | ``` 510 | 511 | 또한 `-g` 옵션을 사용해 전역적으로 설치하는 것도 가능합니다. 512 | 513 | 그런다음 `package.json`에 다음과 같은 라인을 추가합니다: 514 | ```json 515 | { 516 | "scripts": { 517 | "start": "fastify start server.js" 518 | } 519 | } 520 | ``` 521 | 522 | 그리고 서버 파일을 다음과 같이 생성합니다(s): 523 | ```js 524 | // server.js 525 | 'use strict' 526 | 527 | module.exports = async function (fastify, opts) { 528 | fastify.get('/', async (request, reply) => { 529 | return { hello: 'world' } 530 | }) 531 | } 532 | ``` 533 | 534 | 그런 뒤 서버를 다음과 같이 실행합니다: 535 | ```bash 536 | npm start 537 | ``` 538 | 539 | ### 발표자료 및 영상 540 | 541 | 542 | - 슬라이드 543 | - [HTTP 서버를 엄청나게 빠른 속도로 구현해보기](https://mcollina.github.io/take-your-http-server-to-ludicrous-speed) 544 | by [@mcollina](https://github.com/mcollina) 545 | - [만약 HTTP가 빨라질 수 있다면?](https://delvedor.github.io/What-if-I-told-you-that-HTTP-can-be-fast) 546 | by [@delvedor](https://github.com/delvedor) 547 | 548 | - 영상 549 | - [HTTP 서버를 엄청나게 빠른 속도로 구현해보기](https://www.youtube.com/watch?v=5z46jJZNe8k) by 550 | [@mcollina](https://github.com/mcollina) 551 | - [만약 HTTP가 빨라질 수 있다면?](https://www.webexpo.net/prague2017/talk/what-if-i-told-you-that-http-can-be-fast/) 552 | by [@delvedor](https://github.com/delvedor) 553 | -------------------------------------------------------------------------------- /docs/Guides/Index.md: -------------------------------------------------------------------------------- 1 |

Fastify

2 | 3 | ## 가이드 목차 4 | 5 | 6 | 이 목차는 알파벳 순서로 정렬되어 있습니다. (역자 주: 번역 시 정렬 순서는 원본을 따랐습니다.) 7 | 8 | + [벤치마킹](./Benchmarking.md): 이 가이드에서는 Fastify를 기반으로 애플리케이션을 벤치마킹하는 방법을 소개합니다. 9 | + [기여](./Contributing.md): Fastify 개발에 참여하는 방법을 자세히 설명하고, 프로젝트의 코드 스타일과 호환되는 환경을 설정하는 방법을 보여줍니다. 10 | + [요청 처리 지연하기](./Delay-Accepting-Requests.md): 애플리케이션에서 특정 조건이 충족될 때까지 특정 경로에 대한 요청 처리를 지연하는 방법에 대한 실용적인 가이드입니다. 이 가이드는 [`훅`](../Reference/Hooks.md), [`데코레이터`](../Reference/Decorators.md), 11 | 그리고 [`플러그인`](../Reference/Plugins.md)을 사용하여 문제를 해결하는 데 중점을 둡니다. 12 | + [생태계](./Ecosystem.md): 모든 핵심 플러그인과 알려진 많은 커뮤니티 플러그인을 나열합니다. 13 | + [유연한 스키마](./Fluent-Schema.md): JSON Schema 작성이 Fluent API로 작성되고 Fastify에서 사용되는 방법을 보여줍니다. 14 | + [시작하기](./Getting-Started.md): Fastify에 대한 소개 자습서입니다. 초보자는 여기에서부터 시작해야 합니다. 15 | + [마이그레이션 가이드 (v4)](./Migration-Guide-V4.md): 이전 버전에서 Fastify v4로 마이그레이션하는 방법을 자세히 설명합니다. 16 | + [마이그레이션 가이드 (v3)](./Migration-Guide-V3.md): 이전 버전에서 Fastify v3으로 마이그레이션하는 방법을 자세히 설명합니다. 17 | + [플러그인 가이드](./Plugins-Guide.md): Fastify 플러그인 작성에 대해 비공식적으로 소개합니다. 18 | + [프로토타입 중독 공격](./Prototype-Poisoning.md): 프로토타입 중독 공격이 어떻게 작동하고, 완화되는지 설명합니다. 19 | + [권장 사항](./Recommendations.md): Fastify를 프로덕션 환경에 배포하는 방법에 대한 권장 사항입니다. 20 | + [서버리스](./Serverless.md): 다양한 FaaS(Function as a Service) 환경에서 Fastify 애플리케이션을 배포하는 방법에 대한 세부 정보입니다. 21 | + [스타일 가이드](./Style-Guide.md): 공식 문서에 기여하려는 사람들을 위해 Fastify 공식 문서에서 사용하는 작문 스타일을 설명합니다. 22 | + [테스트](./Testing.md): Fastify 애플리케이션에 대한 단위 테스트를 작성하는 방법을 설명합니다. 23 | + [플러그인 작성](./Write-Plugin.md): Fastify 팀이 Fastify 플러그인 작성을 위한 우수 사례로 간주하는 가이드라인 모음입니다. -------------------------------------------------------------------------------- /docs/Guides/Migration-Guide-V3.md: -------------------------------------------------------------------------------- 1 | # V3 Migration Guide 2 | 3 | This guide is intended to help with migration from Fastify v2 to v3. 4 | 5 | Before beginning please ensure that any deprecation warnings from v2 are fixed. 6 | All v2 deprecations have been removed and they will no longer work after 7 | upgrading. ([#1750](https://github.com/fastify/fastify/pull/1750)) 8 | 9 | ## Breaking changes 10 | 11 | ### Changed middleware support ([#2014](https://github.com/fastify/fastify/pull/2014)) 12 | 13 | From Fastify v3, middleware support does not come out-of-the-box with the 14 | framework itself. 15 | 16 | If you use Express middleware in your application, please install and register 17 | the [`@fastify/express`](https://github.com/fastify/fastify-express) or 18 | [`@fastify/middie`](https://github.com/fastify/middie) plugin before doing so. 19 | 20 | **v2:** 21 | 22 | ```js 23 | // Using the Express `cors` middleware in Fastify v2. 24 | fastify.use(require('cors')()); 25 | ``` 26 | 27 | **v3:** 28 | 29 | ```js 30 | // Using the Express `cors` middleware in Fastify v3. 31 | await fastify.register(require('@fastify/express')); 32 | fastify.use(require('cors')()); 33 | ``` 34 | 35 | ### Changed logging serialization ([#2017](https://github.com/fastify/fastify/pull/2017)) 36 | 37 | The logging [Serializers](../Reference/Logging.md) have been updated to now 38 | Fastify [`Request`](../Reference/Request.md) and 39 | [`Reply`](../Reference/Reply.md) objects instead of native ones. 40 | 41 | Any custom serializers must be updated if they rely upon `request` or `reply` 42 | properties that are present on the native objects but not the Fastify objects. 43 | 44 | **v2:** 45 | 46 | ```js 47 | const fastify = require('fastify')({ 48 | logger: { 49 | serializers: { 50 | res(res) { 51 | return { 52 | statusCode: res.statusCode, 53 | customProp: res.customProp 54 | }; 55 | } 56 | } 57 | } 58 | }); 59 | ``` 60 | 61 | **v3:** 62 | 63 | ```js 64 | const fastify = require('fastify')({ 65 | logger: { 66 | serializers: { 67 | res(reply) { 68 | return { 69 | statusCode: reply.statusCode, // No change required 70 | customProp: reply.raw.customProp // Log custom property from res object 71 | }; 72 | } 73 | } 74 | } 75 | }); 76 | ``` 77 | 78 | ### Changed schema substitution ([#2023](https://github.com/fastify/fastify/pull/2023)) 79 | 80 | The non-standard `replace-way` shared schema support has been removed. This 81 | feature has been replaced with JSON Schema specification compliant `$ref` based 82 | substitution. To help understand this change read [Validation and Serialization 83 | in Fastify 84 | v3](https://dev.to/eomm/validation-and-serialization-in-fastify-v3-2e8l). 85 | 86 | **v2:** 87 | 88 | ```js 89 | const schema = { 90 | body: 'schemaId#' 91 | }; 92 | fastify.route({ method, url, schema, handler }); 93 | ``` 94 | 95 | **v3:** 96 | 97 | ```js 98 | const schema = { 99 | body: { 100 | $ref: 'schemaId#' 101 | } 102 | }; 103 | fastify.route({ method, url, schema, handler }); 104 | ``` 105 | 106 | ### Changed schema validation options ([#2023](https://github.com/fastify/fastify/pull/2023)) 107 | 108 | The `setSchemaCompiler` and `setSchemaResolver` options have been replaced with 109 | the `setValidatorCompiler` to enable future tooling improvements. To help 110 | understand this change read [Validation and Serialization in Fastify 111 | v3](https://dev.to/eomm/validation-and-serialization-in-fastify-v3-2e8l). 112 | 113 | **v2:** 114 | 115 | ```js 116 | const fastify = Fastify(); 117 | const ajv = new AJV(); 118 | ajv.addSchema(schemaA); 119 | ajv.addSchema(schemaB); 120 | 121 | fastify.setSchemaCompiler(schema => ajv.compile(schema)); 122 | fastify.setSchemaResolver(ref => ajv.getSchema(ref).schema); 123 | ``` 124 | 125 | **v3:** 126 | 127 | ```js 128 | const fastify = Fastify(); 129 | const ajv = new AJV(); 130 | ajv.addSchema(schemaA); 131 | ajv.addSchema(schemaB); 132 | 133 | fastify.setValidatorCompiler(({ schema, method, url, httpPart }) => 134 | ajv.compile(schema) 135 | ); 136 | ``` 137 | 138 | ### Changed preParsing hook behavior ([#2286](https://github.com/fastify/fastify/pull/2286)) 139 | 140 | From Fastify v3, the behavior of the `preParsing` hook will change slightly 141 | to support request payload manipulation. 142 | 143 | The hook now takes an additional argument, `payload`, and therefore the new hook 144 | signature is `fn(request, reply, payload, done)` or `async fn(request, reply, 145 | payload)`. 146 | 147 | The hook can optionally return a new stream via `done(null, stream)` or 148 | returning the stream in case of async functions. 149 | 150 | If the hook returns a new stream, it will be used instead of the original one in 151 | subsequent hooks. A sample use case for this is handling compressed requests. 152 | 153 | The new stream should add the `receivedEncodedLength` property to the stream 154 | that should reflect the actual data size received from the client. For instance, 155 | in a compressed request it should be the size of the compressed payload. This 156 | property can (and should) be dynamically updated during `data` events. 157 | 158 | The old syntax of Fastify v2 without payload is supported but it is deprecated. 159 | 160 | ### Changed hooks behavior ([#2004](https://github.com/fastify/fastify/pull/2004)) 161 | 162 | From Fastify v3, the behavior of `onRoute` and `onRegister` hooks will change 163 | slightly to support hook encapsulation. 164 | 165 | - `onRoute` - The hook will be called asynchronously. The hook is now inherited 166 | when registering a new plugin within the same encapsulation scope. Thus, this 167 | hook should be registered _before_ registering any plugins. 168 | - `onRegister` - Same as the onRoute hook. The only difference is that now the 169 | very first call will no longer be the framework itself, but the first 170 | registered plugin. 171 | 172 | ### Changed Content Type Parser syntax ([#2286](https://github.com/fastify/fastify/pull/2286)) 173 | 174 | In Fastify v3 the content type parsers now have a single signature for parsers. 175 | 176 | The new signatures are `fn(request, payload, done)` or `async fn(request, 177 | payload)`. Note that `request` is now a Fastify request, not an 178 | `IncomingMessage`. The payload is, by default, a stream. If the `parseAs` option 179 | is used in `addContentTypeParser`, then `payload` reflects the option value 180 | (string or buffer). 181 | 182 | The old signatures `fn(req, [done])` or `fn(req, payload, [done])` (where `req` 183 | is `IncomingMessage`) are still supported but are deprecated. 184 | 185 | ### Changed TypeScript support 186 | 187 | The type system was changed in Fastify version 3. The new type system introduces 188 | generic constraining and defaulting, plus a new way to define schema types such 189 | as a request body, querystring, and more! 190 | 191 | **v2:** 192 | 193 | ```ts 194 | interface PingQuerystring { 195 | foo?: number; 196 | } 197 | 198 | interface PingParams { 199 | bar?: string; 200 | } 201 | 202 | interface PingHeaders { 203 | a?: string; 204 | } 205 | 206 | interface PingBody { 207 | baz?: string; 208 | } 209 | 210 | server.get( 211 | '/ping/:bar', 212 | opts, 213 | (request, reply) => { 214 | console.log(request.query); // This is of type `PingQuerystring` 215 | console.log(request.params); // This is of type `PingParams` 216 | console.log(request.headers); // This is of type `PingHeaders` 217 | console.log(request.body); // This is of type `PingBody` 218 | } 219 | ); 220 | ``` 221 | 222 | **v3:** 223 | 224 | ```ts 225 | server.get<{ 226 | Querystring: PingQuerystring; 227 | Params: PingParams; 228 | Headers: PingHeaders; 229 | Body: PingBody; 230 | }>('/ping/:bar', opts, async (request, reply) => { 231 | console.log(request.query); // This is of type `PingQuerystring` 232 | console.log(request.params); // This is of type `PingParams` 233 | console.log(request.headers); // This is of type `PingHeaders` 234 | console.log(request.body); // This is of type `PingBody` 235 | }); 236 | ``` 237 | 238 | ### Manage uncaught exception ([#2073](https://github.com/fastify/fastify/pull/2073)) 239 | 240 | In sync route handlers, if an error was thrown the server crashed by design 241 | without calling the configured `.setErrorHandler()`. This has changed and now 242 | all unexpected errors in sync and async routes are managed. 243 | 244 | **v2:** 245 | 246 | ```js 247 | fastify.setErrorHandler((error, request, reply) => { 248 | // this is NOT called 249 | reply.send(error) 250 | }) 251 | fastify.get('/', (request, reply) => { 252 | const maybeAnArray = request.body.something ? [] : 'I am a string' 253 | maybeAnArray.substr() // Thrown: [].substr is not a function and crash the server 254 | }) 255 | ``` 256 | 257 | **v3:** 258 | 259 | ```js 260 | fastify.setErrorHandler((error, request, reply) => { 261 | // this IS called 262 | reply.send(error) 263 | }) 264 | fastify.get('/', (request, reply) => { 265 | const maybeAnArray = request.body.something ? [] : 'I am a string' 266 | maybeAnArray.substr() // Thrown: [].substr is not a function, but it is handled 267 | }) 268 | ``` 269 | 270 | ## Further additions and improvements 271 | 272 | - Hooks now have consistent context regardless of how they are registered 273 | ([#2005](https://github.com/fastify/fastify/pull/2005)) 274 | - Deprecated `request.req` and `reply.res` for 275 | [`request.raw`](../Reference/Request.md) and 276 | [`reply.raw`](../Reference/Reply.md) 277 | ([#2008](https://github.com/fastify/fastify/pull/2008)) 278 | - Removed `modifyCoreObjects` option 279 | ([#2015](https://github.com/fastify/fastify/pull/2015)) 280 | - Added [`connectionTimeout`](../Reference/Server.md#factory-connection-timeout) 281 | option ([#2086](https://github.com/fastify/fastify/pull/2086)) 282 | - Added [`keepAliveTimeout`](../Reference/Server.md#factory-keep-alive-timeout) 283 | option ([#2086](https://github.com/fastify/fastify/pull/2086)) 284 | - Added async-await support for [plugins](../Reference/Plugins.md#async-await) 285 | ([#2093](https://github.com/fastify/fastify/pull/2093)) 286 | - Added the feature to throw object as error 287 | ([#2134](https://github.com/fastify/fastify/pull/2134)) 288 | -------------------------------------------------------------------------------- /docs/Guides/Migration-Guide-V4.md: -------------------------------------------------------------------------------- 1 | # V4 Migration Guide 2 | 3 | This guide is intended to help with migration from Fastify v3 to v4. 4 | 5 | Before migrating to v4, please ensure that you have fixed all deprecation 6 | warnings from v3. All v3 deprecations have been removed and they will no longer 7 | work after upgrading. 8 | 9 | ## Breaking Changes 10 | 11 | ### Error handling composition ([#3261](https://github.com/fastify/fastify/pull/3261)) 12 | 13 | When an error is thrown in a async error handler function, 14 | the upper-level error handler is executed if set. 15 | If there is not a upper-level error handler, the default will 16 | be executed as it was previously. 17 | 18 | ``` 19 | import Fastify from 'fastify' 20 | 21 | const fastify = Fastify() 22 | 23 | fastify.register(async fastify => { 24 | fastify.setErrorHandler(async err => { 25 | console.log(err.message) // 'kaboom' 26 | throw new Error('caught') 27 | }) 28 | 29 | fastify.get('/encapsulated', async () => { 30 | throw new Error('kaboom') 31 | }) 32 | }) 33 | 34 | fastify.setErrorHandler(async err => { 35 | console.log(err.message) // 'caught' 36 | throw new Error('wrapped') 37 | }) 38 | 39 | const res = await fastify.inject('/encapsulated') 40 | console.log(res.json().message) // 'wrapped' 41 | ``` 42 | 43 | ### Deprecation of `app.use()` ([#3506](https://github.com/fastify/fastify/pull/3506)) 44 | 45 | Starting this version of Fastify, we have deprecated the use of `app.use()`. We 46 | have decided not to support the use of middlewares. Both 47 | [`@fastify/middie`](https://github.com/fastify/middie) and 48 | [`@fastify/express`](https://github.com/fastify/fastify-express) will still be 49 | there and maintained. Use Fastify's [hooks](../Reference/Hooks.md) instead. 50 | 51 | ### `reply.res` moved to `reply.raw` 52 | 53 | If you previously used the `reply.res` attribute to access the underlying Request 54 | object you'll instead need to depend on `reply.raw`. 55 | 56 | ### Need to `return reply` to signal a "fork" of the promise chain 57 | 58 | In some situations, like when a response is sent asynchronously or when you're 59 | just not explicitly returning a response, you'll need to return the `reply` 60 | argument from your router handler. 61 | 62 | ### `exposeHeadRoutes` true by default 63 | 64 | Starting from v4, all the `GET` routes will create a sibling `HEAD` route. 65 | You can revert this behaviour by setting the server's option `exposeHeadRoutes` 66 | to `false`. 67 | 68 | ### Synchronous route definitions 69 | 70 | The route registration has been made synchronous from v4. 71 | This change was done to provide better error reporting for route definition. 72 | As a result if you specify an `onRoute` hook in a plugin you should either: 73 | * wrap your routes in a plugin (recommended) 74 | * use `await register(...)` 75 | 76 | For example refactor this: 77 | ``` 78 | fastify.register((instance, opts, done) => { 79 | instance.addHook('onRoute', (routeOptions) => { 80 | const { path, method } = routeOptions; 81 | console.log({ path, method }); 82 | }); 83 | done(); 84 | }); 85 | ``` 86 | Into this: 87 | ``` 88 | await fastify.register((instance, opts, done) => { 89 | instance.addHook('onRoute', (routeOptions) => { 90 | const { path, method } = routeOptions; 91 | console.log({ path, method }); 92 | }); 93 | done(); 94 | }); 95 | ``` 96 | 97 | ## Non Breaking Changes 98 | 99 | ### Change of schema for multiple types 100 | 101 | 102 | Since Fastify v4 has upgraded to Ajv v8. The "type" keywords with multiple types 103 | (other than with "null") are prohibited. Read more 104 | ['here'](https://ajv.js.org/strict-mode.html#strict-types) 105 | 106 | You may encounter a console warning such as 107 | 108 | ``` 109 | strict mode: use allowUnionTypes to allow union type keyword at "#/properties/image" (strictTypes) 110 | ``` 111 | So schemas like below will need to be changed from 112 | ``` 113 | type: 'object', 114 | properties: { 115 | api_key: { type: 'string' }, 116 | image: { type: ['object', 'array'] } 117 | } 118 | } 119 | ``` 120 | to 121 | 122 | ``` 123 | type: 'object', 124 | properties: { 125 | api_key: { type: 'string' }, 126 | image: { 127 | anyOf: [ 128 | { type: 'array' }, 129 | { type: 'object' } 130 | ] 131 | } 132 | } 133 | ``` 134 | 135 | ### Add `reply.trailers` methods ([#3794](https://github.com/fastify/fastify/pull/3794)) 136 | 137 | Fastify now supports the [HTTP Trailer] response headers. 138 | 139 | 140 | [HTTP Trailer]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Trailer 141 | -------------------------------------------------------------------------------- /docs/Guides/Plugins-Guide.md: -------------------------------------------------------------------------------- 1 |

Fastify

2 | 3 | # The hitchhiker's guide to plugins 4 | First of all, `DON'T PANIC`! 5 | 6 | Fastify was built from the beginning to be an extremely modular system. We built 7 | a powerful API that allows you to add methods and utilities to Fastify by 8 | creating a namespace. We built a system that creates an encapsulation model, 9 | which allows you to split your application into multiple microservices at any 10 | moment, without the need to refactor the entire application. 11 | 12 | **Table of contents** 13 | - [The hitchhiker's guide to plugins](#the-hitchhikers-guide-to-plugins) 14 | - [Register](#register) 15 | - [Decorators](#decorators) 16 | - [Hooks](#hooks) 17 | - [How to handle encapsulation and 18 | distribution](#how-to-handle-encapsulation-and-distribution) 19 | - [ESM support](#esm-support) 20 | - [Handle errors](#handle-errors) 21 | - [Custom errors](#custom-errors) 22 | - [Emit Warnings](#emit-warnings) 23 | - [Let's start!](#lets-start) 24 | 25 | ## Register 26 | 27 | 28 | As with JavaScript, where everything is an object, in Fastify everything is a 29 | plugin. 30 | 31 | Your routes, your utilities, and so on are all plugins. To add a new plugin, 32 | whatever its functionality may be, in Fastify you have a nice and unique API: 33 | [`register`](../Reference/Plugins.md). 34 | ```js 35 | fastify.register( 36 | require('./my-plugin'), 37 | { options } 38 | ) 39 | ``` 40 | `register` creates a new Fastify context, which means that if you perform any 41 | changes on the Fastify instance, those changes will not be reflected in the 42 | context's ancestors. In other words, encapsulation! 43 | 44 | *Why is encapsulation important?* 45 | 46 | Well, let's say you are creating a new disruptive startup, what do you do? You 47 | create an API server with all your stuff, everything in the same place, a 48 | monolith! 49 | 50 | Ok, you are growing very fast and you want to change your architecture and try 51 | microservices. Usually, this implies a huge amount of work, because of cross 52 | dependencies and a lack of separation of concerns in the codebase. 53 | 54 | Fastify helps you in that regard. Thanks to the encapsulation model, it will 55 | completely avoid cross dependencies and will help you structure your code into 56 | cohesive blocks. 57 | 58 | *Let's return to how to correctly use `register`.* 59 | 60 | As you probably know, the required plugins must expose a single function with 61 | the following signature 62 | ```js 63 | module.exports = function (fastify, options, done) {} 64 | ``` 65 | Where `fastify` is the encapsulated Fastify instance, `options` is the options 66 | object, and `done` is the function you **must** call when your plugin is ready. 67 | 68 | Fastify's plugin model is fully reentrant and graph-based, it handles 69 | asynchronous code without any problems and it enforces both the load and close 70 | order of plugins. *How?* Glad you asked, check out 71 | [`avvio`](https://github.com/mcollina/avvio)! Fastify starts loading the plugin 72 | __after__ `.listen()`, `.inject()` or `.ready()` are called. 73 | 74 | Inside a plugin you can do whatever you want, register routes, utilities (we 75 | will see this in a moment) and do nested registers, just remember to call `done` 76 | when everything is set up! 77 | ```js 78 | module.exports = function (fastify, options, done) { 79 | fastify.get('/plugin', (request, reply) => { 80 | reply.send({ hello: 'world' }) 81 | }) 82 | 83 | done() 84 | } 85 | ``` 86 | 87 | Well, now you know how to use the `register` API and how it works, but how do we 88 | add new functionality to Fastify and even better, share them with other 89 | developers? 90 | 91 | ## Decorators 92 | 93 | 94 | Okay, let's say that you wrote a utility that is so good that you decided to 95 | make it available along with all your code. How would you do it? Probably 96 | something like the following: 97 | ```js 98 | // your-awesome-utility.js 99 | module.exports = function (a, b) { 100 | return a + b 101 | } 102 | ``` 103 | ```js 104 | const util = require('./your-awesome-utility') 105 | console.log(util('that is ', 'awesome')) 106 | ``` 107 | Now you will import your utility in every file you need it in. (And do not 108 | forget that you will probably also need it in your tests). 109 | 110 | Fastify offers you a more elegant and comfortable way to do this, *decorators*. 111 | Creating a decorator is extremely easy, just use the 112 | [`decorate`](../Reference/Decorators.md) API: 113 | ```js 114 | fastify.decorate('util', (a, b) => a + b) 115 | ``` 116 | Now you can access your utility just by calling `fastify.util` whenever you need 117 | it - even inside your test. 118 | 119 | And here starts the magic; do you remember how just now we were talking about 120 | encapsulation? Well, using `register` and `decorate` in conjunction enable 121 | exactly that, let me show you an example to clarify this: 122 | ```js 123 | fastify.register((instance, opts, done) => { 124 | instance.decorate('util', (a, b) => a + b) 125 | console.log(instance.util('that is ', 'awesome')) 126 | 127 | done() 128 | }) 129 | 130 | fastify.register((instance, opts, done) => { 131 | console.log(instance.util('that is ', 'awesome')) // This will throw an error 132 | 133 | done() 134 | }) 135 | ``` 136 | Inside the second register call `instance.util` will throw an error because 137 | `util` exists only inside the first register context. 138 | 139 | Let's step back for a moment and dig deeper into this: every time you use the 140 | `register` API, a new context is created which avoids the negative situations 141 | mentioned above. 142 | 143 | Do note that encapsulation applies to the ancestors and siblings, but not the 144 | children. 145 | ```js 146 | fastify.register((instance, opts, done) => { 147 | instance.decorate('util', (a, b) => a + b) 148 | console.log(instance.util('that is ', 'awesome')) 149 | 150 | fastify.register((instance, opts, done) => { 151 | console.log(instance.util('that is ', 'awesome')) // This will not throw an error 152 | done() 153 | }) 154 | 155 | done() 156 | }) 157 | 158 | fastify.register((instance, opts, done) => { 159 | console.log(instance.util('that is ', 'awesome')) // This will throw an error 160 | 161 | done() 162 | }) 163 | ``` 164 | *Take home message: if you need a utility that is available in every part of 165 | your application, take care that it is declared in the root scope of your 166 | application. If that is not an option, you can use the `fastify-plugin` utility 167 | as described [here](#distribution).* 168 | 169 | `decorate` is not the only API that you can use to extend the server 170 | functionality, you can also use `decorateRequest` and `decorateReply`. 171 | 172 | *`decorateRequest` and `decorateReply`? Why do we need them if we already have 173 | `decorate`?* 174 | 175 | Good question, we added them to make Fastify more developer-friendly. Let's see 176 | an example: 177 | ```js 178 | fastify.decorate('html', payload => { 179 | return generateHtml(payload) 180 | }) 181 | 182 | fastify.get('/html', (request, reply) => { 183 | reply 184 | .type('text/html') 185 | .send(fastify.html({ hello: 'world' })) 186 | }) 187 | ``` 188 | It works, but it could be much better! 189 | ```js 190 | fastify.decorateReply('html', function (payload) { 191 | this.type('text/html') // This is the 'Reply' object 192 | this.send(generateHtml(payload)) 193 | }) 194 | 195 | fastify.get('/html', (request, reply) => { 196 | reply.html({ hello: 'world' }) 197 | }) 198 | ``` 199 | Reminder that the `this` keyword is not available on *arrow functions*, 200 | so when passing functions in *`decorateReply`* and *`decorateRequest`* as 201 | a utility that also needs access to the `request` and `reply` instance, 202 | a function that is defined using the `function` keyword is needed instead 203 | of an *arrow function expression*. 204 | 205 | In the same way you can do this for the `request` object: 206 | ```js 207 | fastify.decorate('getHeader', (req, header) => { 208 | return req.headers[header] 209 | }) 210 | 211 | fastify.addHook('preHandler', (request, reply, done) => { 212 | request.isHappy = fastify.getHeader(request.raw, 'happy') 213 | done() 214 | }) 215 | 216 | fastify.get('/happiness', (request, reply) => { 217 | reply.send({ happy: request.isHappy }) 218 | }) 219 | ``` 220 | Again, it works, but it can be much better! 221 | ```js 222 | fastify.decorateRequest('setHeader', function (header) { 223 | this.isHappy = this.headers[header] 224 | }) 225 | 226 | fastify.decorateRequest('isHappy', false) // This will be added to the Request object prototype, yay speed! 227 | 228 | fastify.addHook('preHandler', (request, reply, done) => { 229 | request.setHeader('happy') 230 | done() 231 | }) 232 | 233 | fastify.get('/happiness', (request, reply) => { 234 | reply.send({ happy: request.isHappy }) 235 | }) 236 | ``` 237 | 238 | We have seen how to extend server functionality and how to handle the 239 | encapsulation system, but what if you need to add a function that must be 240 | executed whenever the server "[emits](../Reference/Lifecycle.md)" an 241 | event? 242 | 243 | ## Hooks 244 | 245 | 246 | You just built an amazing utility, but now you need to execute that for every 247 | request, this is what you will likely do: 248 | ```js 249 | fastify.decorate('util', (request, key, value) => { request[key] = value }) 250 | 251 | fastify.get('/plugin1', (request, reply) => { 252 | fastify.util(request, 'timestamp', new Date()) 253 | reply.send(request) 254 | }) 255 | 256 | fastify.get('/plugin2', (request, reply) => { 257 | fastify.util(request, 'timestamp', new Date()) 258 | reply.send(request) 259 | }) 260 | ``` 261 | I think we all agree that this is terrible. Repeated code, awful readability and 262 | it cannot scale. 263 | 264 | So what can you do to avoid this annoying issue? Yes, you are right, use a 265 | [hook](../Reference/Hooks.md)! 266 | 267 | ```js 268 | fastify.decorate('util', (request, key, value) => { request[key] = value }) 269 | 270 | fastify.addHook('preHandler', (request, reply, done) => { 271 | fastify.util(request, 'timestamp', new Date()) 272 | done() 273 | }) 274 | 275 | fastify.get('/plugin1', (request, reply) => { 276 | reply.send(request) 277 | }) 278 | 279 | fastify.get('/plugin2', (request, reply) => { 280 | reply.send(request) 281 | }) 282 | ``` 283 | Now for every request, you will run your utility. You can register as many hooks 284 | as you need. 285 | 286 | Sometimes you want a hook that should be executed for just a subset of routes, 287 | how can you do that? Yep, encapsulation! 288 | 289 | ```js 290 | fastify.register((instance, opts, done) => { 291 | instance.decorate('util', (request, key, value) => { request[key] = value }) 292 | 293 | instance.addHook('preHandler', (request, reply, done) => { 294 | instance.util(request, 'timestamp', new Date()) 295 | done() 296 | }) 297 | 298 | instance.get('/plugin1', (request, reply) => { 299 | reply.send(request) 300 | }) 301 | 302 | done() 303 | }) 304 | 305 | fastify.get('/plugin2', (request, reply) => { 306 | reply.send(request) 307 | }) 308 | ``` 309 | Now your hook will run just for the first route! 310 | 311 | As you probably noticed by now, `request` and `reply` are not the standard 312 | Nodejs *request* and *response* objects, but Fastify's objects. 313 | 314 | 315 | ## How to handle encapsulation and distribution 316 | 317 | 318 | Perfect, now you know (almost) all of the tools that you can use to extend 319 | Fastify. Nevertheless, chances are that you came across one big issue: how is 320 | distribution handled? 321 | 322 | The preferred way to distribute a utility is to wrap all your code inside a 323 | `register`. Using this, your plugin can support asynchronous bootstrapping 324 | *(since `decorate` is a synchronous API)*, in the case of a database connection 325 | for example. 326 | 327 | *Wait, what? Didn't you tell me that `register` creates an encapsulation and 328 | that the stuff I create inside will not be available outside?* 329 | 330 | Yes, I said that. However, what I didn't tell you is that you can tell Fastify 331 | to avoid this behavior with the 332 | [`fastify-plugin`](https://github.com/fastify/fastify-plugin) module. 333 | ```js 334 | const fp = require('fastify-plugin') 335 | const dbClient = require('db-client') 336 | 337 | function dbPlugin (fastify, opts, done) { 338 | dbClient.connect(opts.url, (err, conn) => { 339 | fastify.decorate('db', conn) 340 | done() 341 | }) 342 | } 343 | 344 | module.exports = fp(dbPlugin) 345 | ``` 346 | You can also tell `fastify-plugin` to check the installed version of Fastify, in 347 | case you need a specific API. 348 | 349 | As we mentioned earlier, Fastify starts loading its plugins __after__ 350 | `.listen()`, `.inject()` or `.ready()` are called and as such, __after__ they 351 | have been declared. This means that, even though the plugin may inject variables 352 | to the external Fastify instance via [`decorate`](../Reference/Decorators.md), 353 | the decorated variables will not be accessible before calling `.listen()`, 354 | `.inject()` or `.ready()`. 355 | 356 | In case you rely on a variable injected by a preceding plugin and want to pass 357 | that in the `options` argument of `register`, you can do so by using a function 358 | instead of an object: 359 | ```js 360 | const fastify = require('fastify')() 361 | const fp = require('fastify-plugin') 362 | const dbClient = require('db-client') 363 | 364 | function dbPlugin (fastify, opts, done) { 365 | dbClient.connect(opts.url, (err, conn) => { 366 | fastify.decorate('db', conn) 367 | done() 368 | }) 369 | } 370 | 371 | fastify.register(fp(dbPlugin), { url: 'https://example.com' }) 372 | fastify.register(require('your-plugin'), parent => { 373 | return { connection: parent.db, otherOption: 'foo-bar' } 374 | }) 375 | ``` 376 | In the above example, the `parent` variable of the function passed in as the 377 | second argument of `register` is a copy of the **external Fastify instance** 378 | that the plugin was registered at. This means that we can access any 379 | variables that were injected by preceding plugins in the order of declaration. 380 | 381 | ## ESM support 382 | 383 | 384 | ESM is supported as well from [Node.js 385 | `v13.3.0`](https://nodejs.org/api/esm.html) and above! Just export your plugin 386 | as ESM module and you are good to go! 387 | 388 | ```js 389 | // plugin.mjs 390 | async function plugin (fastify, opts) { 391 | fastify.get('/', async (req, reply) => { 392 | return { hello: 'world' } 393 | }) 394 | } 395 | 396 | export default plugin 397 | ``` 398 | __Note__: Fastify does not support named imports within an ESM context. Instead, 399 | the `default` export is available. 400 | 401 | ```js 402 | // server.mjs 403 | import Fastify from 'fastify' 404 | 405 | const fastify = Fastify() 406 | 407 | ///... 408 | 409 | fastify.listen({ port: 3000 }, (err, address) => { 410 | if (err) { 411 | fastify.log.error(err) 412 | process.exit(1) 413 | } 414 | }) 415 | ``` 416 | 417 | ## Handle errors 418 | 419 | 420 | One of your plugins may fail during startup. Maybe you expect it 421 | and you have a custom logic that will be triggered in that case. How can you 422 | implement this? The `after` API is what you need. `after` simply registers a 423 | callback that will be executed just after a register, and it can take up to 424 | three parameters. 425 | 426 | The callback changes based on the parameters you are giving: 427 | 428 | 1. If no parameter is given to the callback and there is an error, that error 429 | will be passed to the next error handler. 430 | 1. If one parameter is given to the callback, that parameter will be the error 431 | object. 432 | 1. If two parameters are given to the callback, the first will be the error 433 | object; the second will be the done callback. 434 | 1. If three parameters are given to the callback, the first will be the error 435 | object, the second will be the top-level context unless you have specified 436 | both server and override, in that case, the context will be what the override 437 | returns, and the third the done callback. 438 | 439 | Let's see how to use it: 440 | ```js 441 | fastify 442 | .register(require('./database-connector')) 443 | .after(err => { 444 | if (err) throw err 445 | }) 446 | ``` 447 | 448 | ## Custom errors 449 | 450 | 451 | If your plugin needs to expose custom errors, you can easily generate consistent 452 | error objects across your codebase and plugins with the 453 | [`@fastify/error`](https://github.com/fastify/fastify-error) module. 454 | 455 | ```js 456 | const createError = require('@fastify/error') 457 | const CustomError = createError('ERROR_CODE', 'message') 458 | console.log(new CustomError()) 459 | ``` 460 | 461 | ## Emit Warnings 462 | 463 | 464 | If you want to deprecate an API, or you want to warn the user about a specific 465 | use case, you can use the 466 | [`process-warning`](https://github.com/fastify/process-warning) module. 467 | 468 | ```js 469 | const warning = require('process-warning')() 470 | warning.create('FastifyDeprecation', 'FST_ERROR_CODE', 'message') 471 | warning.emit('FST_ERROR_CODE') 472 | ``` 473 | 474 | ## Let's start! 475 | 476 | 477 | Awesome, now you know everything you need to know about Fastify and its plugin 478 | system to start building your first plugin, and please if you do, tell us! We 479 | will add it to the [*ecosystem*](https://github.com/fastify/fastify#ecosystem) 480 | section of our documentation! 481 | 482 | If you want to see some real-world examples, check out: 483 | - [`@fastify/view`](https://github.com/fastify/point-of-view) Templates 484 | rendering (*ejs, pug, handlebars, marko*) plugin support for Fastify. 485 | - [`@fastify/mongodb`](https://github.com/fastify/fastify-mongodb) Fastify 486 | MongoDB connection plugin, with this you can share the same MongoDB connection 487 | pool in every part of your server. 488 | - [`@fastify/multipart`](https://github.com/fastify/fastify-multipart) Multipart 489 | support for Fastify 490 | - [`@fastify/helmet`](https://github.com/fastify/fastify-helmet) Important 491 | security headers for Fastify 492 | 493 | 494 | *Do you feel like something is missing here? Let us know! :)* 495 | -------------------------------------------------------------------------------- /docs/Guides/Prototype-Poisoning.md: -------------------------------------------------------------------------------- 1 | > The following is an article written by Eran Hammer. 2 | > It is reproduced here for posterity [with permission](https://github.com/fastify/fastify/issues/1426#issuecomment-817957913). 3 | > It has been reformatted from the original HTML source to Markdown source, 4 | > but otherwise remains the same. The original HTML can be retrieved from the 5 | > above permission link. 6 | 7 | ## A Tale of (prototype) Poisoning 8 | 9 | 10 | This story is a behind-the-scenes look at the process and drama created by a 11 | particularity interesting web security issue. It is also a perfect illustration 12 | of the efforts required to maintain popular pieces of open source software and 13 | the limitations of existing communication channels. 14 | 15 | But first, if you use a JavaScript framework to process incoming JSON data, take 16 | a moment to read up on [Prototype 17 | Poisoning](https://medium.com/intrinsic/javascript-prototype-poisoning-vulnerabilities-in-the-wild-7bc15347c96) 18 | in general, and the specific [technical 19 | details](https://github.com/hapijs/hapi/issues/3916) of this issue. I'll explain 20 | it all in a bit, but since this could be a critical issue, you might want to 21 | verify your own code first. While this story is focused on a specific framework, 22 | any solution that uses `JSON.parse()` to process external data is potentially at 23 | risk. 24 | 25 | ### BOOM 26 | 27 | 28 | Our story begins with a bang. 29 | 30 | The engineering team at Lob (long time generous supporters of my work!) reported 31 | a critical security vulnerability they identified in our data validation 32 | module — [joi](https://github.com/hapijs/joi). They provided some technical 33 | details and a proposed solution. 34 | 35 | The main purpose of a data validation library is to ensure the output fully 36 | complies with the rules defined. If it doesn't, validation fails. If it passes, 37 | your can blindly trust that the data you are working with is safe. In fact, most 38 | developers treat validated input as completely safe from a system integrity 39 | perspective. This is crucial. 40 | 41 | In our case, the Lob team provided an example where some data was able to sneak 42 | by the validation logic and pass through undetected. This is the worst possible 43 | defect a validation library can have. 44 | 45 | ### Prototype in a nutshell 46 | 47 | 48 | To understand this story, you need to understand how JavaScript works a bit. 49 | Every object in JavaScript can have a prototype. It is a set of methods and 50 | properties it "inherits" from another object. I put inherits in quotes because 51 | JavaScript isn't really an object oriented language. 52 | 53 | A long time ago, for a bunch of irrelevant reasons, someone decided that it 54 | would be a good idea to use the special property name `__proto__` to access (and 55 | set) an object's prototype. This has since been deprecated but nevertheless, 56 | fully supported. 57 | 58 | To demonstrate: 59 | 60 | ``` 61 | > const a = { b: 5 }; 62 | > a.b; 63 | 5 64 | > a.__proto__ = { c: 6 }; 65 | > a.c; 66 | 6 67 | > a; 68 | { b: 5 } 69 | ``` 70 | 71 | As you can see, the object doesn't have a `c` property, but its prototype does. 72 | When validating the object, the validation library ignores the prototype and 73 | only validates the object's own properties. This allows `c` to sneak in via the 74 | prototype. 75 | 76 | Another important part of this story is the way `JSON.parse()` — a utility 77 | provided by the language to convert JSON formatted text into objects  —  handles 78 | this magic `__proto__` property name. 79 | 80 | ``` 81 | > const text = '{ "b": 5, "__proto__": { "c": 6 } }'; 82 | > const a = JSON.parse(text); 83 | > a; 84 | { b: 5, __proto__: { c: 6 } } 85 | ``` 86 | 87 | Notice how `a` has a `__proto__` property. This is not a prototype reference. It 88 | is a simple object property key, just like `b`. As we've seen from the first 89 | example, we can't actually create this key through assignment as that invokes 90 | the prototype magic and sets an actual prototype. `JSON.parse()` however, sets a 91 | simple property with that poisonous name. 92 | 93 | By itself, the object created by `JSON.parse()` is perfectly safe. It doesn't 94 | have a prototype of its own. It has a seemingly harmless property that just 95 | happens to overlap with a built-in JavaScript magic name. 96 | 97 | However, other methods are not as lucky: 98 | 99 | ``` 100 | > const x = Object.assign({}, a); 101 | > x; 102 | { b: 5} 103 | > x.c; 104 | 6; 105 | ``` 106 | 107 | If we take the `a` object created earlier by `JSON.parse()` and pass it to the 108 | helpful `Object.assign()` method (used to perform a shallow copy of all the top 109 | level properties of `a` into the provided empty `{}` object), the magic 110 | `__proto__` property "leaks" and becomes `x` 's actual prototype. 111 | 112 | Surprise! 113 | 114 | Put together, if you get some external text input, parse it with `JSON.parse()` 115 | then perform some simple manipulation of that object (say, shallow clone and add 116 | an `id` ), and then pass it to our validation library, anything passed through 117 | via `__proto__` would sneak in undetected. 118 | 119 | ### Oh joi! 120 | 121 | 122 | The first question is, of course, why does the validation module **joi** ignore 123 | the prototype and let potentially harmful data through? We asked ourselves the 124 | same question and our instant thought was "it was an oversight". A bug. A really 125 | big mistake. The joi module should not have allowed this to happen. But… 126 | 127 | While joi is used primarily for validating web input data, it also has a 128 | significant user base using it to validate internal objects, some of which have 129 | prototypes. The fact that joi ignores the prototype is a helpful "feature". It 130 | allows validating the object's own properties while ignoring what could be a 131 | very complicated prototype structure (with many methods and literal properties). 132 | 133 | Any solution at the joi level would mean breaking some currently working code. 134 | 135 | ### The right thing 136 | 137 | 138 | At this point, we were looking at a devastatingly bad security vulnerability. 139 | Right up there in the upper echelons of epic security failures. All we knew is 140 | that our extremely popular data validation library fails to block harmful data, 141 | and that this data is trivial to sneak through. All you need to do is add 142 | `__proto__` and some crap to a JSON input and send it on its way to an 143 | application built using our tools. 144 | 145 | (Dramatic pause) 146 | 147 | We knew we had to fix joi to prevent this but given the scale of this issue, we 148 | had to do it in a way that will put a fix out without drawing too much attention 149 | to it — without making it too easy to exploit — at least for a few days until 150 | most systems received the update. 151 | 152 | Sneaking a fix isn't the hardest thing to accomplish. If you combine it with an 153 | otherwise purposeless refactor of the code, and throw in a few unrelated bug 154 | fixes and maybe a cool new feature, you can publish a new version without 155 | drawing attention to the real issue being fixed. 156 | 157 | The problem was, the right fix was going to break valid use cases. You see, joi 158 | has no way of knowing if you want it to ignore the prototype you set, or block 159 | the prototype set by an attacker. A solution that fixes the exploit will break 160 | code and breaking code tends to get a lot of attention. 161 | 162 | On the other hand, if we released a proper ([semantically 163 | versioned](https://semver.org/)) fix, mark it as a breaking change, and add a 164 | new API to explicitly tell joi what you want it to do with the prototype, we 165 | will share with the world how to exploit this vulnerability while also making it 166 | more time consuming for systems to upgrade (breaking changes never get applied 167 | automatically by build tools). 168 | 169 | Lose — Lose. 170 | 171 | ### A detour 172 | 173 | 174 | While the issue at hand was about incoming request payloads, we had to pause and 175 | check if it could also impact data coming via the query string, cookies, and 176 | headers. Basically, anything that gets serialized into objects from text. 177 | 178 | We quickly confirmed node default query string parser was fine as well as its 179 | header parser. I identified one potential issue with base64-encoded JSON cookies 180 | as well as the usage of custom query string parsers. We also wrote some tests to 181 | confirm that the most popular third-party query string parser  — 182 | [qs](https://www.npmjs.com/package/qs) —  was not vulnerable (it is not!). 183 | 184 | ### A development 185 | 186 | 187 | Throughout this triage, we just assumed that the offending input with its 188 | poisoned prototype was coming into joi from hapi, the web framework connecting 189 | the hapi.js ecosystem. Further investigation by the Lob team found that the 190 | problem was a bit more nuanced. 191 | 192 | hapi used `JSON.parse()` to process incoming data. It first set the result 193 | object as a `payload` property of the incoming request, and then passed that 194 | same object for validation by joi before being passed to the application 195 | business logic for processing. Since `JSON.parse()` doesn't actually leak the 196 | `__proto__` property, it would arrive to joi with an invalid key and fail 197 | validation. 198 | 199 | However, hapi provides two extension points where the payload data can be 200 | inspected (and processed) prior to validation. It is all properly documented and 201 | well understood by most developers. The extension points are there to allow you 202 | to interact with the raw inputs prior to validation for legitimate (and often 203 | security related) reasons. 204 | 205 | If during one of these two extension points, a developer used `Object.assign()` 206 | or a similar method on the payload, the `__proto__` property would leak and 207 | become an actual prototype. 208 | 209 | ### Sigh of relief 210 | 211 | 212 | We were now dealing with a much different level of awfulness. Manipulating the 213 | payload object prior to validation is not common which meant this was no longer 214 | a doomsday scenario. It was still potentially catastrophic but the exposure 215 | dropped from every joi user to some very specific implementations. 216 | 217 | We were no longer looking at a secretive joi release. The issue in joi is still 218 | there, but we can now address it properly with a new API and breaking release 219 | over the next few weeks. 220 | 221 | We also knew that we can easily mitigate this vulnerability at the framework 222 | level since it knows which data is coming from the outside and which is 223 | internally generated. The framework is really the only piece that can protect 224 | developers against making such unexpected mistakes. 225 | 226 | ### Good news, bad news, no news? 227 | 228 | 229 | The good news was that this wasn't our fault. It wasn't a bug in hapi or joi. It 230 | was only possible through a complex combination of actions that was not unique 231 | to hapi or joi. This can happen with every other JavaScript framework. If hapi 232 | is broken, then the world is broken. 233 | 234 | Great — we solved the blame game. 235 | 236 | The bad news is that when there is nothing to blame (other than JavaScript 237 | itself), it is much harder getting it fixed. 238 | 239 | The first question people ask once a security issue is found is if there is 240 | going to be a CVE published. A CVE — Common Vulnerabilities and Exposures — is a 241 | [database](https://cve.mitre.org/) of known security issues. It is a critical 242 | component of web security. The benefit of publishing a CVE is that it 243 | immediately triggers alarms and informs and often breaks automated builds until 244 | the issue is resolved. 245 | 246 | But what do we pin this to? 247 | 248 | Probably, nothing. We are still debating whether we should tag some versions of 249 | hapi with a warning. The "we" is the node security process. Since we now have a 250 | new version of hapi that mitigate the problem by default, it can be considered a 251 | fix. But because the fix isn't to a problem in hapi itself, it is not exactly 252 | kosher to declare older versions harmful. 253 | 254 | Publishing an advisory on previous versions of hapi for the sole purpose of 255 | nudging people into awareness and upgrade is an abuse of the advisory process. 256 | I'm personally fine with abusing it for the purpose of improving security but 257 | that's not my call. As of this writing, it is still being debated. 258 | 259 | ### The solution business 260 | 261 | 262 | Mitigating the issue wasn't hard. Making it scale and safe was a bit more 263 | involved. Since we knew where harmful data can enter the system, and we knew 264 | where we used the problematic `JSON.parse()` we could replace it with a safe 265 | implementation. 266 | 267 | One problem. Validating data can be costly and we are now planning on validating 268 | every incoming JSON text. The built-in `JSON.parse()` implementation is fast. 269 | Really really fast. It is unlikely we can build a replacement that will be more 270 | secure and anywhere as fast. Especially not overnight and without introducing 271 | new bugs. 272 | 273 | It was obvious we were going to wrap the existing `JSON.parse()` method with 274 | some additional logic. We just had to make sure it was not adding too much 275 | overhead. This isn't just a performance consideration but also a security one. 276 | If we make it easy to slow down a system by simply sending specific data, we 277 | make it easy to execute a [DoS 278 | attack](https://en.wikipedia.org/wiki/Denial-of-service_attack) at very low 279 | cost. 280 | 281 | I came up with a stupidly simple solution: first parse the text using the 282 | existing tools. If this didn't fail, scan the original raw text for the 283 | offending string "__proto__". Only if we find it, perform an actual scan of the 284 | object. We can't block every reference to "__proto__" — sometimes it is 285 | perfectly valid value (like when writing about it here and sending this text 286 | over to Medium for publication). 287 | 288 | This made the "happy path" practically as fast as before. It just added one 289 | function call, a quick text scan (again, very fast built-in implementation), and 290 | a conditional return. The solution had negligible impact on the vast majority of 291 | data expected to pass through it. 292 | 293 | Next problem. The prototype property doesn't have to be at the top level of the 294 | incoming object. It can be nested deep inside. This means we cannot just check 295 | for the presence of it at the top level. We need to recursively iterate through 296 | the object. 297 | 298 | While recursive functions are a favorite tool, they could be disastrous when 299 | writing security-conscious code. You see, recursive function increase the size 300 | of the runtime call stack. The more times you loop, the longer the call stack 301 | gets. At some point — KABOOM— you reach the maximum length and the process dies. 302 | 303 | If you cannot guarantee the shape of the incoming data, recursive iteration 304 | becomes an open threat. An attacker only needs to craft a deep enough object to 305 | crash your servers. 306 | 307 | I used a flat loop implementation that is both more memory efficient (less 308 | function calls, less passing of temporary arguments) and more secure. I am not 309 | pointing this out to brag, but to highlight how basic engineering practices can 310 | create (or avoid) security pitfalls. 311 | 312 | ### Putting it to the test 313 | 314 | 315 | I sent the code to two people. First to [Nathan 316 | LaFreniere](https://github.com/nlf) to double check the security properties of 317 | the solution, and then to [Matteo Collina](https://github.com/mcollina) to 318 | review the performance. They are among the very best at what they do and often 319 | my go-to people. 320 | 321 | The performance benchmarks confirmed that the "happy path" was practically 322 | unaffected. The interesting findings was that removing the offending values was 323 | faster then throwing an exception. This raised the question of what should be 324 | the default behavior of the new module — which I called 325 | [**bourne**](https://github.com/hapijs/bourne) —  error or sanitize. 326 | 327 | The concern, again, was exposing the application to a DoS attack. If sending a 328 | request with `__proto__` makes things 500% slower, that could be an easy vector 329 | to exploit. But after a bit more testing we confirmed that sending **any** 330 | invalid JSON text was creating a very similar cost. 331 | 332 | In other words, if you parse JSON, invalid values are going to cost you more, 333 | regardless of what makes them invalid. It is also important to remember that 334 | while the benchmark showed the significant % cost of scanning suspected objects, 335 | the actual cost in CPU time was still in the fraction of milliseconds. Important 336 | to note and measure but not actually harmful. 337 | 338 | ### hapi ever-after 339 | 340 | 341 | There are a bunch of things to be grateful for. 342 | 343 | The initial disclosure by the Lob team was perfect. It was reported privately, 344 | to the right people, with the right information. They followed up with 345 | additional findings, and gave us the time and space to resolve it the right way. 346 | Lob also was a major sponsor of my work on hapi over the years and that 347 | financial support is critical to allow everything else to happen. More on that 348 | in a bit. 349 | 350 | Triage was stressful but staffed with the right people. Having folks like 351 | [Nicolas Morel](https://github.com/Marsup), Nathan, and Matteo, available and 352 | eager to help is critical. This isn't easy to deal with without the pressure, 353 | but with it, mistakes are likely without proper team collaboration. 354 | 355 | We got lucky with the actual vulnerability. What started up looking like a 356 | catastrophic problem, ended up being a delicate but straight-forward problem to 357 | address. 358 | 359 | We also got lucky by having full access to mitigate it at the source — didn't 360 | need to send emails to some unknown framework maintainer and hope for a quick 361 | answer. hapi's total control over all of its dependencies proved its usefulness 362 | and security again. Not using [hapi](https://hapi.dev)? [Maybe you 363 | should](https://hueniverse.com/why-you-should-consider-hapi-6163689bd7c2). 364 | 365 | ### The after in happy ever-after 366 | 367 | 368 | This is where I have to take advantage of this incident to reiterate the cost 369 | and need for sustainable and secure open source. 370 | 371 | My time alone on this one issue exceeded 20 hours. That's half a working week. 372 | It came at the end of a month were I already spent over 30 hours publishing a 373 | new major release of hapi (most of the work was done in December). This puts me 374 | at a personal financial loss of over $5000 this month (I had to cut back on paid 375 | client work to make time for it). 376 | 377 | If you rely on code I maintain, this is exactly the level of support, quality, 378 | and commitment you want (and lets be honest — expect). Most of you take it for 379 | granted — not just my work but the work of hundreds of other dedicated open 380 | source maintainers. 381 | 382 | Because this work is important, I decided to try and make it not just 383 | financially sustainable but to grow and expand it. There is so much to improve. 384 | This is exactly what motivates me to implement the new [commercial licensing 385 | plan](https://web.archive.org/web/20190201220503/https://hueniverse.com/on-hapi-licensing-a-preview-f982662ee898) 386 | coming in March. You can read more about it 387 | [here](https://web.archive.org/web/20190201220503/https://hueniverse.com/on-hapi-licensing-a-preview-f982662ee898). 388 | 389 | Of all the time consuming things, security is at the very top. I hope this story 390 | successfully conveyed not just the technical details, but also the human drama and 391 | what it takes to keep the web secure. 392 | -------------------------------------------------------------------------------- /docs/Guides/Recommendations.md: -------------------------------------------------------------------------------- 1 |

Fastify

2 | 3 | ## Recommendations 4 | 5 | This document contains a set of recommendations when using Fastify. 6 | 7 | - [Use A Reverse Proxy](#use-a-reverse-proxy) 8 | - [HAProxy](#haproxy) 9 | - [Nginx](#nginx) 10 | - [Kubernetes](#kubernetes) 11 | 12 | ## Use A Reverse Proxy 13 | 14 | 15 | Node.js is an early adopter of frameworks shipping with an easy-to-use web 16 | server within the standard library. Previously, with languages like PHP or 17 | Python, one would need either a web server with specific support for the 18 | language or the ability to set up some sort of [CGI gateway][cgi] that works 19 | with the language. With Node.js, one can write an application that _directly_ 20 | handles HTTP requests. As a result, the temptation is to write applications that 21 | handle requests for multiple domains, listen on multiple ports (i.e. HTTP _and_ 22 | HTTPS), and then expose these applications directly to the Internet to handle 23 | requests. 24 | 25 | The Fastify team **strongly** considers this to be an anti-pattern and extremely 26 | bad practice: 27 | 28 | 1. It adds unnecessary complexity to the application by diluting its focus. 29 | 2. It prevents [horizontal scalability][scale-horiz]. 30 | 31 | See [Why should I use a Reverse Proxy if Node.js is Production Ready?][why-use] 32 | for a more thorough discussion of why one should opt to use a reverse proxy. 33 | 34 | For a concrete example, consider the situation where: 35 | 36 | 1. The app needs multiple instances to handle load. 37 | 1. The app needs TLS termination. 38 | 1. The app needs to redirect HTTP requests to HTTPS. 39 | 1. The app needs to serve multiple domains. 40 | 1. The app needs to serve static resources, e.g. jpeg files. 41 | 42 | There are many reverse proxy solutions available, and your environment may 43 | dictate the solution to use, e.g. AWS or GCP. Given the above, we could use 44 | [HAProxy][haproxy] or [Nginx][nginx] to solve these requirements: 45 | 46 | ### HAProxy 47 | 48 | ```conf 49 | # The global section defines base HAProxy (engine) instance configuration. 50 | global 51 | log /dev/log syslog 52 | maxconn 4096 53 | chroot /var/lib/haproxy 54 | user haproxy 55 | group haproxy 56 | 57 | # Set some baseline TLS options. 58 | tune.ssl.default-dh-param 2048 59 | ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11 60 | ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS 61 | ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 62 | ssl-default-server-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS 63 | 64 | # Each defaults section defines options that will apply to each subsequent 65 | # subsection until another defaults section is encountered. 66 | defaults 67 | log global 68 | mode http 69 | option httplog 70 | option dontlognull 71 | retries 3 72 | option redispatch 73 | # The following option makes haproxy close connections to backend servers 74 | # instead of keeping them open. This can alleviate unexpected connection 75 | # reset errors in the Node process. 76 | option http-server-close 77 | maxconn 2000 78 | timeout connect 5000 79 | timeout client 50000 80 | timeout server 50000 81 | 82 | # Enable content compression for specific content types. 83 | compression algo gzip 84 | compression type text/html text/plain text/css application/javascript 85 | 86 | # A "frontend" section defines a public listener, i.e. an "http server" 87 | # as far as clients are concerned. 88 | frontend proxy 89 | # The IP address here would be the _public_ IP address of the server. 90 | # Here, we use a private address as an example. 91 | bind 10.0.0.10:80 92 | # This redirect rule will redirect all traffic that is not TLS traffic 93 | # to the same incoming request URL on the HTTPS port. 94 | redirect scheme https code 308 if !{ ssl_fc } 95 | # Technically this use_backend directive is useless since we are simply 96 | # redirecting all traffic to this frontend to the HTTPS frontend. It is 97 | # merely included here for completeness sake. 98 | use_backend default-server 99 | 100 | # This frontend defines our primary, TLS only, listener. It is here where 101 | # we will define the TLS certificates to expose and how to direct incoming 102 | # requests. 103 | frontend proxy-ssl 104 | # The `/etc/haproxy/certs` directory in this example contains a set of 105 | # certificate PEM files that are named for the domains the certificates are 106 | # issued for. When HAProxy starts, it will read this directory, load all of 107 | # the certificates it finds here, and use SNI matching to apply the correct 108 | # certificate to the connection. 109 | bind 10.0.0.10:443 ssl crt /etc/haproxy/certs 110 | 111 | # Here we define rule pairs to handle static resources. Any incoming request 112 | # that has a path starting with `/static`, e.g. 113 | # `https://one.example.com/static/foo.jpeg`, will be redirected to the 114 | # static resources server. 115 | acl is_static path -i -m beg /static 116 | use_backend static-backend if is_static 117 | 118 | # Here we define rule pairs to direct requests to appropriate Node.js 119 | # servers based on the requested domain. The `acl` line is used to match 120 | # the incoming hostname and define a boolean indicating if it is a match. 121 | # The `use_backend` line is used to direct the traffic if the boolean is 122 | # true. 123 | acl example1 hdr_sub(Host) one.example.com 124 | use_backend example1-backend if example1 125 | 126 | acl example2 hdr_sub(Host) two.example.com 127 | use_backend example2-backend if example2 128 | 129 | # Finally, we have a fallback redirect if none of the requested hosts 130 | # match the above rules. 131 | default_backend default-server 132 | 133 | # A "backend" is used to tell HAProxy where to request information for the 134 | # proxied request. These sections are where we will define where our Node.js 135 | # apps live and any other servers for things like static assets. 136 | backend default-server 137 | # In this example we are defaulting unmatched domain requests to a single 138 | # backend server for all requests. Notice that the backend server does not 139 | # have to be serving TLS requests. This is called "TLS termination": the TLS 140 | # connection is "terminated" at the reverse proxy. 141 | # It is possible to also proxy to backend servers that are themselves serving 142 | # requests over TLS, but that is outside the scope of this example. 143 | server server1 10.10.10.2:80 144 | 145 | # This backend configuration will serve requests for `https://one.example.com` 146 | # by proxying requests to three backend servers in a round-robin manner. 147 | backend example1-backend 148 | server example1-1 10.10.11.2:80 149 | server example1-2 10.10.11.2:80 150 | server example2-2 10.10.11.3:80 151 | 152 | # This one serves requests for `https://two.example.com` 153 | backend example2-backend 154 | server example2-1 10.10.12.2:80 155 | server example2-2 10.10.12.2:80 156 | server example2-3 10.10.12.3:80 157 | 158 | # This backend handles the static resources requests. 159 | backend static-backend 160 | server static-server1 10.10.9.2:80 161 | ``` 162 | 163 | [cgi]: https://en.wikipedia.org/wiki/Common_Gateway_Interface 164 | [scale-horiz]: https://en.wikipedia.org/wiki/Scalability#Horizontal 165 | [why-use]: https://web.archive.org/web/20190821102906/https://medium.com/intrinsic/why-should-i-use-a-reverse-proxy-if-node-js-is-production-ready-5a079408b2ca 166 | [haproxy]: https://www.haproxy.org/ 167 | 168 | ### Nginx 169 | 170 | ```nginx 171 | # This upstream block groups 3 servers into one named backend fastify_app 172 | # with 2 primary servers distributed via round-robin 173 | # and one backup which is used when the first 2 are not reachable 174 | # This also assumes your fastify servers are listening on port 80. 175 | # more info: https://nginx.org/en/docs/http/ngx_http_upstream_module.html 176 | upstream fastify_app { 177 | server 10.10.11.1:80; 178 | server 10.10.11.2:80; 179 | server 10.10.11.3:80 backup; 180 | } 181 | 182 | # This server block asks NGINX to respond with a redirect when 183 | # an incoming request from port 80 (typically plain HTTP), to 184 | # the same request URL but with HTTPS as protocol. 185 | # This block is optional, and usually used if you are handling 186 | # SSL termination in NGINX, like in the example here. 187 | server { 188 | # default server is a special parameter to ask NGINX 189 | # to set this server block to the default for this address/port 190 | # which in this case is any address and port 80 191 | listen 80 default_server; 192 | listen [::]:80 default_server; 193 | 194 | # With a server_name directive you can also ask NGINX to 195 | # use this server block only with matching server name(s) 196 | # listen 80; 197 | # listen [::]:80; 198 | # server_name example.tld; 199 | 200 | # This matches all paths from the request and responds with 201 | # the redirect mentioned above. 202 | location / { 203 | return 301 https://$host$request_uri; 204 | } 205 | } 206 | 207 | # This server block asks NGINX to respond to requests from 208 | # port 443 with SSL enabled and accept HTTP/2 connections. 209 | # This is where the request is then proxied to the fastify_app 210 | # server group via port 3000. 211 | server { 212 | # This listen directive asks NGINX to accept requests 213 | # coming to any address, port 443, with SSL, and HTTP/2 214 | # if possible. 215 | listen 443 ssl http2 default_server; 216 | listen [::]:443 ssl http2 default_server; 217 | 218 | # With a server_name directive you can also ask NGINX to 219 | # use this server block only with matching server name(s) 220 | # listen 443 ssl http2; 221 | # listen [::]:443 ssl http2; 222 | # server_name example.tld; 223 | 224 | # Your SSL/TLS certificate (chain) and secret key in the PEM format 225 | ssl_certificate /path/to/fullchain.pem; 226 | ssl_certificate_key /path/to/private.pem; 227 | 228 | # A generic best practice baseline for based 229 | # on https://ssl-config.mozilla.org/ 230 | ssl_session_timeout 1d; 231 | ssl_session_cache shared:FastifyApp:10m; 232 | ssl_session_tickets off; 233 | 234 | # This tells NGINX to only accept TLS 1.3, which should be fine 235 | # with most modern browsers including IE 11 with certain updates. 236 | # If you want to support older browsers you might need to add 237 | # additional fallback protocols. 238 | ssl_protocols TLSv1.3; 239 | ssl_prefer_server_ciphers off; 240 | 241 | # This adds a header that tells browsers to only ever use HTTPS 242 | # with this server. 243 | add_header Strict-Transport-Security "max-age=63072000" always; 244 | 245 | # The following directives are only necessary if you want to 246 | # enable OCSP Stapling. 247 | ssl_stapling on; 248 | ssl_stapling_verify on; 249 | ssl_trusted_certificate /path/to/chain.pem; 250 | 251 | # Custom nameserver to resolve upstream server names 252 | # resolver 127.0.0.1; 253 | 254 | # This section matches all paths and proxies it to the backend server 255 | # group specified above. Note the additional headers that forward 256 | # information about the original request. You might want to set 257 | # trustProxy to the address of your NGINX server so the X-Forwarded 258 | # fields are used by fastify. 259 | location / { 260 | # more info: https://nginx.org/en/docs/http/ngx_http_proxy_module.html 261 | proxy_http_version 1.1; 262 | proxy_cache_bypass $http_upgrade; 263 | proxy_set_header Upgrade $http_upgrade; 264 | proxy_set_header Connection 'upgrade'; 265 | proxy_set_header Host $host; 266 | proxy_set_header X-Real-IP $remote_addr; 267 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 268 | proxy_set_header X-Forwarded-Proto $scheme; 269 | 270 | # This is the directive that proxies requests to the specified server. 271 | # If you are using an upstream group, then you do not need to specify a port. 272 | # If you are directly proxying to a server e.g. 273 | # proxy_pass http://127.0.0.1:3000 then specify a port. 274 | proxy_pass http://fastify_app; 275 | } 276 | } 277 | ``` 278 | 279 | [nginx]: https://nginx.org/ 280 | 281 | ## Kubernetes 282 | 283 | 284 | The `readinessProbe` uses [(by 285 | default](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes)) 286 | the pod IP as the hostname. Fastify listens on `127.0.0.1` by default. The probe 287 | will not be able to reach the application in this case. To make it work, 288 | the application must listen on `0.0.0.0` or specify a custom hostname in 289 | the `readinessProbe.httpGet` spec, as per the following example: 290 | 291 | ```yaml 292 | readinessProbe: 293 | httpGet: 294 | path: /health 295 | port: 4000 296 | initialDelaySeconds: 30 297 | periodSeconds: 30 298 | timeoutSeconds: 3 299 | successThreshold: 1 300 | failureThreshold: 5 301 | -------------------------------------------------------------------------------- /docs/Guides/Serverless.md: -------------------------------------------------------------------------------- 1 |

Serverless

2 | 3 | Run serverless applications and REST APIs using your existing Fastify 4 | application. By default, Fastify will not work on your serverless platform of 5 | choice, you will need to make some small changes to fix this. This document 6 | contains a small guide for the most popular serverless providers and how to use 7 | Fastify with them. 8 | 9 | #### Should you use Fastify in a serverless platform? 10 | 11 | That is up to you! Keep in mind that functions as a service should always use 12 | small and focused functions, but you can also run an entire web application with 13 | them. It is important to remember that the bigger the application the slower the 14 | initial boot will be. The best way to run Fastify applications in serverless 15 | environments is to use platforms like Google Cloud Run, AWS Fargate, and Azure 16 | Container Instances, where the server can handle multiple requests at the same 17 | time and make full use of Fastify's features. 18 | 19 | One of the best features of using Fastify in serverless applications is the ease 20 | of development. In your local environment, you will always run the Fastify 21 | application directly without the need for any additional tools, while the same 22 | code will be executed in your serverless platform of choice with an additional 23 | snippet of code. 24 | 25 | ### Contents 26 | 27 | - [AWS](#aws) 28 | - [Google Cloud Functions](#google-cloud-functions) 29 | - [Google Cloud Run](#google-cloud-run) 30 | - [Netlify Lambda](#netlify-lambda) 31 | - [Vercel](#vercel) 32 | 33 | ## AWS 34 | 35 | To integrate with AWS, you have two choices of library: 36 | 37 | - Using [@fastify/aws-lambda](https://github.com/fastify/aws-lambda-fastify) 38 | which only adds API Gateway support but has heavy optimizations for fastify. 39 | - Using [@h4ad/serverless-adapter](https://github.com/H4ad/serverless-adapter) 40 | which is a little slower as it creates an HTTP request for each AWS event but 41 | has support for more AWS services such as: AWS SQS, AWS SNS and others. 42 | 43 | So you can decide which option is best for you, but you can test both libraries. 44 | 45 | ### Using @fastify/aws-lambda 46 | 47 | The sample provided allows you to easily build serverless web 48 | applications/services and RESTful APIs using Fastify on top of AWS Lambda and 49 | Amazon API Gateway. 50 | 51 | #### app.js 52 | 53 | ```js 54 | const fastify = require('fastify'); 55 | 56 | function init() { 57 | const app = fastify(); 58 | app.get('/', (request, reply) => reply.send({ hello: 'world' })); 59 | return app; 60 | } 61 | 62 | if (require.main === module) { 63 | // called directly i.e. "node app" 64 | init().listen({ port: 3000 }, (err) => { 65 | if (err) console.error(err); 66 | console.log('server listening on 3000'); 67 | }); 68 | } else { 69 | // required as a module => executed on aws lambda 70 | module.exports = init; 71 | } 72 | ``` 73 | 74 | When executed in your lambda function we do not need to listen to a specific 75 | port, so we just export the wrapper function `init` in this case. The 76 | [`lambda.js`](#lambdajs) file will use this export. 77 | 78 | When you execute your Fastify application like always, i.e. `node app.js` *(the 79 | detection for this could be `require.main === module`)*, you can normally listen 80 | to your port, so you can still run your Fastify function locally. 81 | 82 | #### lambda.js 83 | 84 | ```js 85 | const awsLambdaFastify = require('@fastify/aws-lambda') 86 | const init = require('./app'); 87 | 88 | const proxy = awsLambdaFastify(init()) 89 | // or 90 | // const proxy = awsLambdaFastify(init(), { binaryMimeTypes: ['application/octet-stream'] }) 91 | 92 | exports.handler = proxy; 93 | // or 94 | // exports.handler = (event, context, callback) => proxy(event, context, callback); 95 | // or 96 | // exports.handler = (event, context) => proxy(event, context); 97 | // or 98 | // exports.handler = async (event, context) => proxy(event, context); 99 | ``` 100 | 101 | We just require 102 | [@fastify/aws-lambda](https://github.com/fastify/aws-lambda-fastify) (make sure 103 | you install the dependency `npm i @fastify/aws-lambda`) and our 104 | [`app.js`](#appjs) file and call the exported `awsLambdaFastify` function with 105 | the `app` as the only parameter. The resulting `proxy` function has the correct 106 | signature to be used as a lambda `handler` function. This way all the incoming 107 | events (API Gateway requests) are passed to the `proxy` function of 108 | [@fastify/aws-lambda](https://github.com/fastify/aws-lambda-fastify). 109 | 110 | #### Example 111 | 112 | An example deployable with 113 | [claudia.js](https://claudiajs.com/tutorials/serverless-express.html) can be 114 | found 115 | [here](https://github.com/claudiajs/example-projects/tree/master/fastify-app-lambda). 116 | 117 | ### Considerations 118 | 119 | - API Gateway does not support streams yet, so you are not able to handle 120 | [streams](../Reference/Reply.md#streams). 121 | - API Gateway has a timeout of 29 seconds, so it is important to provide a reply 122 | during this time. 123 | 124 | #### Beyond API Gateway 125 | 126 | If you need to integrate with more AWS services, take a look at 127 | [@h4ad/serverless-adapter](https://viniciusl.com.br/serverless-adapter/docs/main/frameworks/fastify) 128 | on Fastify to find out how to integrate. 129 | 130 | ## Google Cloud Functions 131 | 132 | ### Creation of Fastify instance 133 | ```js 134 | const fastify = require("fastify")({ 135 | logger: true // you can also define the level passing an object configuration to logger: {level: 'debug'} 136 | }); 137 | ``` 138 | 139 | ### Add Custom `contentTypeParser` to Fastify instance 140 | 141 | As explained [in issue 142 | #946](https://github.com/fastify/fastify/issues/946#issuecomment-766319521), 143 | since the Google Cloud Functions platform parses the body of the request before 144 | it arrives at the Fastify instance, troubling the body request in case of `POST` 145 | and `PATCH` methods, you need to add a custom [`Content-Type 146 | Parser`](../Reference/ContentTypeParser.md) to mitigate this behavior. 147 | 148 | ```js 149 | fastify.addContentTypeParser('application/json', {}, (req, body, done) => { 150 | done(null, body.body); 151 | }); 152 | ``` 153 | 154 | ### Define your endpoint (examples) 155 | 156 | A simple `GET` endpoint: 157 | ```js 158 | fastify.get('/', async (request, reply) => { 159 | reply.send({message: 'Hello World!'}) 160 | }) 161 | ``` 162 | 163 | Or a more complete `POST` endpoint with schema validation: 164 | ```js 165 | fastify.route({ 166 | method: 'POST', 167 | url: '/hello', 168 | schema: { 169 | body: { 170 | type: 'object', 171 | properties: { 172 | name: { type: 'string'} 173 | }, 174 | required: ['name'] 175 | }, 176 | response: { 177 | 200: { 178 | type: 'object', 179 | properties: { 180 | message: {type: 'string'} 181 | } 182 | } 183 | }, 184 | }, 185 | handler: async (request, reply) => { 186 | const { name } = request.body; 187 | reply.code(200).send({ 188 | message: `Hello ${name}!` 189 | }) 190 | } 191 | }) 192 | ``` 193 | 194 | ### Implement and export the function 195 | 196 | Final step, implement the function to handle the request and pass it to Fastify 197 | by emitting `request` event to `fastify.server`: 198 | 199 | ```js 200 | const fastifyFunction = async (request, reply) => { 201 | await fastify.ready(); 202 | fastify.server.emit('request', request, reply) 203 | } 204 | 205 | export.fastifyFunction = fastifyFunction; 206 | ``` 207 | 208 | ### Local test 209 | 210 | Install [Google Functions Framework for 211 | Node.js](https://github.com/GoogleCloudPlatform/functions-framework-nodejs). 212 | 213 | You can install it globally: 214 | ```bash 215 | npm i -g @google-cloud/functions-framework 216 | ``` 217 | 218 | Or as a development library: 219 | ```bash 220 | npm i -D @google-cloud/functions-framework 221 | ``` 222 | 223 | Then you can run your function locally with Functions Framework: 224 | ```bash 225 | npx @google-cloud/functions-framework --target=fastifyFunction 226 | ``` 227 | 228 | Or add this command to your `package.json` scripts: 229 | ```json 230 | "scripts": { 231 | ... 232 | "dev": "npx @google-cloud/functions-framework --target=fastifyFunction" 233 | ... 234 | } 235 | ``` 236 | and run it with `npm run dev`. 237 | 238 | 239 | ### Deploy 240 | ```bash 241 | gcloud functions deploy fastifyFunction \ 242 | --runtime nodejs14 --trigger-http --region $GOOGLE_REGION --allow-unauthenticated 243 | ``` 244 | 245 | #### Read logs 246 | ```bash 247 | gcloud functions logs read 248 | ``` 249 | 250 | #### Example request to `/hello` endpoint 251 | ```bash 252 | curl -X POST https://$GOOGLE_REGION-$GOOGLE_PROJECT.cloudfunctions.net/me \ 253 | -H "Content-Type: application/json" \ 254 | -d '{ "name": "Fastify" }' 255 | {"message":"Hello Fastify!"} 256 | ``` 257 | 258 | ### References 259 | - [Google Cloud Functions - Node.js Quickstart 260 | ](https://cloud.google.com/functions/docs/quickstart-nodejs) 261 | 262 | ## Google Cloud Run 263 | 264 | Unlike AWS Lambda or Google Cloud Functions, Google Cloud Run is a serverless 265 | **container** environment. Its primary purpose is to provide an 266 | infrastructure-abstracted environment to run arbitrary containers. As a result, 267 | Fastify can be deployed to Google Cloud Run with little-to-no code changes from 268 | the way you would write your Fastify app normally. 269 | 270 | *Follow the steps below to deploy to Google Cloud Run if you are already 271 | familiar with gcloud or just follow their 272 | [quickstart](https://cloud.google.com/run/docs/quickstarts/build-and-deploy)*. 273 | 274 | ### Adjust Fastify server 275 | 276 | In order for Fastify to properly listen for requests within the container, be 277 | sure to set the correct port and address: 278 | 279 | ```js 280 | function build() { 281 | const fastify = Fastify({ trustProxy: true }) 282 | return fastify 283 | } 284 | 285 | async function start() { 286 | // Google Cloud Run will set this environment variable for you, so 287 | // you can also use it to detect if you are running in Cloud Run 288 | const IS_GOOGLE_CLOUD_RUN = process.env.K_SERVICE !== undefined 289 | 290 | // You must listen on the port Cloud Run provides 291 | const port = process.env.PORT || 3000 292 | 293 | // You must listen on all IPV4 addresses in Cloud Run 294 | const host = IS_GOOGLE_CLOUD_RUN ? "0.0.0.0" : undefined 295 | 296 | try { 297 | const server = build() 298 | const address = await server.listen({ port, host }) 299 | console.log(`Listening on ${address}`) 300 | } catch (err) { 301 | console.error(err) 302 | process.exit(1) 303 | } 304 | } 305 | 306 | module.exports = build 307 | 308 | if (require.main === module) { 309 | start() 310 | } 311 | ``` 312 | 313 | ### Add a Dockerfile 314 | 315 | You can add any valid `Dockerfile` that packages and runs a Node app. A basic 316 | `Dockerfile` can be found in the official [gcloud 317 | docs](https://github.com/knative/docs/blob/2d654d1fd6311750cc57187a86253c52f273d924/docs/serving/samples/hello-world/helloworld-nodejs/Dockerfile). 318 | 319 | ```Dockerfile 320 | # Use the official Node.js 10 image. 321 | # https://hub.docker.com/_/node 322 | FROM node:10 323 | 324 | # Create and change to the app directory. 325 | WORKDIR /usr/src/app 326 | 327 | # Copy application dependency manifests to the container image. 328 | # A wildcard is used to ensure both package.json AND package-lock.json are copied. 329 | # Copying this separately prevents re-running npm install on every code change. 330 | COPY package*.json ./ 331 | 332 | # Install production dependencies. 333 | RUN npm i --production 334 | 335 | # Copy local code to the container image. 336 | COPY . . 337 | 338 | # Run the web service on container startup. 339 | CMD [ "npm", "start" ] 340 | ``` 341 | 342 | ### Add a .dockerignore 343 | 344 | To keep build artifacts out of your container (which keeps it small and improves 345 | build times) add a `.dockerignore` file like the one below: 346 | 347 | ```.dockerignore 348 | Dockerfile 349 | README.md 350 | node_modules 351 | npm-debug.log 352 | ``` 353 | 354 | ### Submit build 355 | 356 | Next, submit your app to be built into a Docker image by running the following 357 | command (replacing `PROJECT-ID` and `APP-NAME` with your GCP project id and an 358 | app name): 359 | 360 | ```bash 361 | gcloud builds submit --tag gcr.io/PROJECT-ID/APP-NAME 362 | ``` 363 | 364 | ### Deploy Image 365 | 366 | After your image has built, you can deploy it with the following command: 367 | 368 | ```bash 369 | gcloud beta run deploy --image gcr.io/PROJECT-ID/APP-NAME --platform managed 370 | ``` 371 | 372 | Your app will be accessible from the URL GCP provides. 373 | 374 | 375 | ## netlify-lambda 376 | 377 | First, please perform all preparation steps related to **AWS Lambda**. 378 | 379 | Create a folder called `functions`, then create `server.js` (and your endpoint 380 | path will be `server.js`) inside the `functions` folder. 381 | 382 | ### functions/server.js 383 | 384 | ```js 385 | export { handler } from '../lambda.js'; // Change `lambda.js` path to your `lambda.js` path 386 | ``` 387 | 388 | ### netlify.toml 389 | 390 | ```toml 391 | [build] 392 | # This will be run the site build 393 | command = "npm run build:functions" 394 | # This is the directory is publishing to netlify's CDN 395 | # and this is directory of your front of your app 396 | # publish = "build" 397 | # functions build directory 398 | functions = "functions-build" # always appends `-build` folder to your `functions` folder for builds 399 | ``` 400 | 401 | ### webpack.config.netlify.js 402 | 403 | **Do not forget to add this Webpack config, or else problems may occur** 404 | 405 | ```js 406 | const nodeExternals = require('webpack-node-externals'); 407 | const dotenv = require('dotenv-safe'); 408 | const webpack = require('webpack'); 409 | 410 | const env = process.env.NODE_ENV || 'production'; 411 | const dev = env === 'development'; 412 | 413 | if (dev) { 414 | dotenv.config({ allowEmptyValues: true }); 415 | } 416 | 417 | module.exports = { 418 | mode: env, 419 | devtool: dev ? 'eval-source-map' : 'none', 420 | externals: [nodeExternals()], 421 | devServer: { 422 | proxy: { 423 | '/.netlify': { 424 | target: 'http://localhost:9000', 425 | pathRewrite: { '^/.netlify/functions': '' } 426 | } 427 | } 428 | }, 429 | module: { 430 | rules: [] 431 | }, 432 | plugins: [ 433 | new webpack.DefinePlugin({ 434 | 'process.env.APP_ROOT_PATH': JSON.stringify('/'), 435 | 'process.env.NETLIFY_ENV': true, 436 | 'process.env.CONTEXT': env 437 | }) 438 | ] 439 | }; 440 | ``` 441 | 442 | ### Scripts 443 | 444 | Add this command to your `package.json` *scripts* 445 | 446 | ```json 447 | "scripts": { 448 | ... 449 | "build:functions": "netlify-lambda build functions --config ./webpack.config.netlify.js" 450 | ... 451 | } 452 | ``` 453 | 454 | Then it should work fine 455 | 456 | 457 | ## Vercel 458 | 459 | [Vercel](https://vercel.com) provides zero-configuration deployment for Node.js 460 | applications. To use it now, it is as simple as configuring your `vercel.json` 461 | file like the following: 462 | 463 | ```json 464 | { 465 | "rewrites": [ 466 | { 467 | "source": "/(.*)", 468 | "destination": "/api/serverless.js" 469 | } 470 | ] 471 | } 472 | ``` 473 | 474 | Then, write `api/serverless.js` like so: 475 | 476 | ```js 477 | "use strict"; 478 | 479 | // Read the .env file. 480 | import * as dotenv from "dotenv"; 481 | dotenv.config(); 482 | 483 | // Require the framework 484 | import Fastify from "fastify"; 485 | 486 | // Instantiate Fastify with some config 487 | const app = Fastify({ 488 | logger: true, 489 | }); 490 | 491 | // Register your application as a normal plugin. 492 | app.register(import("../src/app")); 493 | 494 | export default async (req, res) => { 495 | await app.ready(); 496 | app.server.emit('request', req, res); 497 | } 498 | ``` 499 | -------------------------------------------------------------------------------- /docs/Guides/Style-Guide.md: -------------------------------------------------------------------------------- 1 | # Fastify Style Guide 2 | 3 | ## Welcome 4 | 5 | Welcome to *Fastify Style Guide*. This guide is here to provide you with a 6 | conventional writing style for users writing developer documentation on our Open 7 | Source framework. Each topic is precise and well explained to help you write 8 | documentation users can easily understand and implement. 9 | 10 | ## Who is this guide for? 11 | 12 | This guide is for anyone who loves to build with Fastify or wants to contribute 13 | to our documentation. You do not need to be an expert in writing technical 14 | documentation. This guide is here to help you. 15 | 16 | Visit the [contribute](https://www.fastify.io/contribute) page on our website or 17 | read the 18 | [CONTRIBUTING.md](https://github.com/fastify/fastify/blob/main/CONTRIBUTING.md) 19 | file on GitHub to join our Open Source folks. 20 | 21 | ## Before you write 22 | 23 | You need to know the following: 24 | 25 | * JavaScript 26 | * Node.js 27 | * Git 28 | * GitHub 29 | * Markdown 30 | * HTTP 31 | * NPM 32 | 33 | ### Consider your Audience 34 | 35 | Before you start writing, think about your audience. In this case, your audience 36 | should already know HTTP, JavaScript, NPM, and Node.js. It is necessary to keep 37 | your readers in mind because they are the ones consuming your content. You want 38 | to give as much useful information as possible. Consider the vital things they 39 | need to know and how they can understand them. Use words and references that 40 | readers can relate to easily. Ask for feedback from the community, it can help 41 | you write better documentation that focuses on the user and what you want to 42 | achieve. 43 | 44 | ### Get straight to the point 45 | 46 | Give your readers a clear and precise action to take. Start with what is most 47 | important. This way, you can help them find what they need faster. Mostly, 48 | readers tend to read the first content on a page, and many will not scroll 49 | further. 50 | 51 | **Example** 52 | 53 | Less like this: Colons are very important to register a parametric path. It lets 54 | the framework know there is a new parameter created. You can place the colon 55 | before the parameter name so the parametric path can be created. 56 | 57 | More Like this: To register a parametric path, put a colon before the parameter 58 | name. Using a colon lets the framework know it is a parametric path and not a 59 | static path. 60 | 61 | ### Avoid adding video or image content 62 | 63 | 64 | Do not add videos or screenshots to the documentation. It is easier to keep 65 | under version control. Videos and images will eventually end up becoming 66 | outdated as new updates keep developing. Instead, make a referral link or a 67 | YouTube video. You can add links by using `[Title](www.websitename.com)` in the 68 | markdown. 69 | 70 | **Example** 71 | 72 | ``` 73 | To learn more about hooks, see [Fastify hooks](https://www.fastify.io/docs/latest/Reference/Hooks/). 74 | ``` 75 | 76 | Result: 77 | >To learn more about hooks, see [Fastify 78 | >hooks](https://www.fastify.io/docs/latest/Reference/Hooks/). 79 | 80 | 81 | 82 | ### Avoid plagiarism 83 | 84 | Make sure you avoid copying other people's work. Keep it as original as 85 | possible. You can learn from what they have done and reference where it is from 86 | if you used a particular quote from their work. 87 | 88 | 89 | ## Word Choice 90 | 91 | There are a few things you need to use and avoid when writing your documentation 92 | to improve readability for readers and make documentation neat, direct, and 93 | clean. 94 | 95 | 96 | ### When to use the second person "you" as the pronoun 97 | 98 | When writing articles or guides, your content should communicate directly to 99 | readers in the second person ("you") addressed form. It is easier to give them 100 | direct instruction on what to do on a particular topic. To see an example, visit 101 | the [Plugins Guide](./Plugins-Guide.md). 102 | 103 | **Example** 104 | 105 | Less like this: we can use the following plugins. 106 | 107 | More like this: You can use the following plugins. 108 | 109 | > According to [Wikipedia](#), ***You*** is usually a second person pronoun. 110 | > Also, used to refer to an indeterminate person, as a more common alternative 111 | > to a very formal indefinite pronoun. 112 | 113 | ## When to avoid the second person "you" as the pronoun 114 | 115 | One of the main rules of formal writing such as reference documentation, or API 116 | documentation, is to avoid the second person ("you") or directly addressing the 117 | reader. 118 | 119 | **Example** 120 | 121 | Less like this: You can use the following recommendation as an example. 122 | 123 | More like this: As an example, the following recommendations should be 124 | referenced. 125 | 126 | To view a live example, refer to the [Decorators](../Reference/Decorators.md) 127 | reference document. 128 | 129 | 130 | ### Avoid using contractions 131 | 132 | Contractions are the shortened version of written and spoken forms of a word, 133 | i.e. using "don't" instead of "do not". Avoid contractions to provide a more 134 | formal tone. 135 | 136 | ### Avoid using condescending terms 137 | 138 | Condescending terms are words that include: 139 | 140 | * Just 141 | * Easy 142 | * Simply 143 | * Basically 144 | * Obviously 145 | 146 | The reader may not find it easy to use Fastify's framework and plugins; avoid 147 | words that make it sound simple, easy, offensive, or insensitive. Not everyone 148 | who reads the documentation has the same level of understanding. 149 | 150 | 151 | ### Starting with a verb 152 | 153 | Mostly start your description with a verb, which makes it simple and precise for 154 | the reader to follow. Prefer using present tense because it is easier to read 155 | and understand than the past or future tense. 156 | 157 | **Example** 158 | 159 | Less like this: There is a need for Node.js to be installed before you can be 160 | able to use Fastify. 161 | 162 | More like this: Install Node.js to make use of Fastify. 163 | 164 | ### Grammatical moods 165 | 166 | Grammatical moods are a great way to express your writing. Avoid sounding too 167 | bossy while making a direct statement. Know when to switch between indicative, 168 | imperative, and subjunctive moods. 169 | 170 | 171 | **Indicative** - Use when making a factual statement or question. 172 | 173 | Example: Since there is no testing framework available, "Fastify recommends ways 174 | to write tests". 175 | 176 | **Imperative** - Use when giving instructions, actions, commands, or when you 177 | write your headings. 178 | 179 | Example: Install dependencies before starting development. 180 | 181 | 182 | **Subjunctive** - Use when making suggestions, hypotheses, or non-factual 183 | statements. 184 | 185 | Example: Reading the documentation on our website is recommended to get 186 | comprehensive knowledge of the framework. 187 | 188 | ### Use **active** voice instead of **passive** 189 | 190 | Using active voice is a more compact and direct way of conveying your 191 | documentation. 192 | 193 | **Example** 194 | 195 | 196 | Passive: The node dependencies and packages are installed by npm. 197 | 198 | Active: npm installs packages and node dependencies. 199 | 200 | ## Writing Style 201 | 202 | ### Documentation titles 203 | 204 | When creating a new guide, API, or reference in the `/docs/` directory, use 205 | short titles that best describe the topic of your documentation. Name your files 206 | in kebab-cases and avoid Raw or camelCase. To learn more about kebab-case you 207 | can visit this medium article on [Case 208 | Styles](https://medium.com/better-programming/string-case-styles-camel-pascal-snake-and-kebab-case-981407998841). 209 | 210 | **Examples**: 211 | 212 | >`hook-and-plugins.md`, 213 | 214 | `adding-test-plugins.md`, 215 | 216 | `removing-requests.md`. 217 | 218 | ### Hyperlinks 219 | 220 | Hyperlinks should have a clear title of what it references. Here is how your 221 | hyperlink should look: 222 | 223 | ```MD 224 | 225 | 226 | // Add clear & brief description 227 | [Fastify Plugins] (https://www.fastify.io/docs/latest/Plugins/) 228 | 229 | 230 | 231 | // incomplete description 232 | [Fastify] (https://www.fastify.io/docs/latest/Plugins/) 233 | 234 | // Adding title in link brackets 235 | [](https://www.fastify.io/docs/latest/Plugins/ "fastify plugin") 236 | 237 | // Empty title 238 | [](https://www.fastify.io/docs/latest/Plugins/) 239 | 240 | // Adding links localhost URLs instead of using code strings (``) 241 | [http://localhost:3000/](http://localhost:3000/) 242 | 243 | ``` 244 | 245 | Include in your documentation as many essential references as possible, but 246 | avoid having numerous links when writing for beginners to avoid distractions. 247 | -------------------------------------------------------------------------------- /docs/Guides/Testing.md: -------------------------------------------------------------------------------- 1 |

Fastify

2 | 3 | ## Testing 4 | 5 | Testing is one of the most important parts of developing an application. Fastify 6 | is very flexible when it comes to testing and is compatible with most testing 7 | frameworks (such as [Tap](https://www.npmjs.com/package/tap), which is used in 8 | the examples below). 9 | 10 | Let's `cd` into a fresh directory called 'testing-example' and type `npm init 11 | -y` in our terminal. 12 | 13 | Run `npm i fastify && npm i tap pino-pretty -D` 14 | 15 | ### Separating concerns makes testing easy 16 | 17 | First, we are going to separate our application code from our server code: 18 | 19 | **app.js**: 20 | 21 | ```js 22 | 'use strict' 23 | 24 | const fastify = require('fastify') 25 | 26 | function build(opts={}) { 27 | const app = fastify(opts) 28 | app.get('/', async function (request, reply) { 29 | return { hello: 'world' } 30 | }) 31 | 32 | return app 33 | } 34 | 35 | module.exports = build 36 | ``` 37 | 38 | **server.js**: 39 | 40 | ```js 41 | 'use strict' 42 | 43 | const server = require('./app')({ 44 | logger: { 45 | level: 'info', 46 | transport: { 47 | target: 'pino-pretty' 48 | } 49 | } 50 | }) 51 | 52 | server.listen({ port: 3000 }, (err, address) => { 53 | if (err) { 54 | server.log.error(err) 55 | process.exit(1) 56 | } 57 | }) 58 | ``` 59 | 60 | ### Benefits of using fastify.inject() 61 | 62 | Fastify comes with built-in support for fake HTTP injection thanks to 63 | [`light-my-request`](https://github.com/fastify/light-my-request). 64 | 65 | Before introducing any tests, we will use the `.inject` method to make a fake 66 | request to our route: 67 | 68 | **app.test.js**: 69 | 70 | ```js 71 | 'use strict' 72 | 73 | const build = require('./app') 74 | 75 | const test = async () => { 76 | const app = build() 77 | 78 | const response = await app.inject({ 79 | method: 'GET', 80 | url: '/' 81 | }) 82 | 83 | console.log('status code: ', response.statusCode) 84 | console.log('body: ', response.body) 85 | } 86 | test() 87 | ``` 88 | 89 | First, our code will run inside an asynchronous function, giving us access to 90 | async/await. 91 | 92 | `.inject` ensures all registered plugins have booted up and our application is 93 | ready to test. Finally, we pass the request method we want to use and a route. 94 | Using await we can store the response without a callback. 95 | 96 | 97 | 98 | Run the test file in your terminal `node app.test.js` 99 | 100 | ```sh 101 | status code: 200 102 | body: {"hello":"world"} 103 | ``` 104 | 105 | 106 | 107 | ### Testing with HTTP injection 108 | 109 | Now we can replace our `console.log` calls with actual tests! 110 | 111 | In your `package.json` change the "test" script to: 112 | 113 | `"test": "tap --reporter=list --watch"` 114 | 115 | **app.test.js**: 116 | 117 | ```js 118 | 'use strict' 119 | 120 | const { test } = require('tap') 121 | const build = require('./app') 122 | 123 | test('requests the "/" route', async t => { 124 | const app = build() 125 | 126 | const response = await app.inject({ 127 | method: 'GET', 128 | url: '/' 129 | }) 130 | t.equal(response.statusCode, 200, 'returns a status code of 200') 131 | }) 132 | ``` 133 | 134 | Finally, run `npm test` in the terminal and see your test results! 135 | 136 | The `inject` method can do much more than a simple GET request to a URL: 137 | ```js 138 | fastify.inject({ 139 | method: String, 140 | url: String, 141 | query: Object, 142 | payload: Object, 143 | headers: Object, 144 | cookies: Object 145 | }, (error, response) => { 146 | // your tests 147 | }) 148 | ``` 149 | 150 | `.inject` methods can also be chained by omitting the callback function: 151 | 152 | ```js 153 | fastify 154 | .inject() 155 | .get('/') 156 | .headers({ foo: 'bar' }) 157 | .query({ foo: 'bar' }) 158 | .end((err, res) => { // the .end call will trigger the request 159 | console.log(res.payload) 160 | }) 161 | ``` 162 | 163 | or in the promisified version 164 | 165 | ```js 166 | fastify 167 | .inject({ 168 | method: String, 169 | url: String, 170 | query: Object, 171 | payload: Object, 172 | headers: Object, 173 | cookies: Object 174 | }) 175 | .then(response => { 176 | // your tests 177 | }) 178 | .catch(err => { 179 | // handle error 180 | }) 181 | ``` 182 | 183 | Async await is supported as well! 184 | ```js 185 | try { 186 | const res = await fastify.inject({ method: String, url: String, payload: Object, headers: Object }) 187 | // your tests 188 | } catch (err) { 189 | // handle error 190 | } 191 | ``` 192 | 193 | #### Another Example: 194 | 195 | **app.js** 196 | ```js 197 | const Fastify = require('fastify') 198 | 199 | function buildFastify () { 200 | const fastify = Fastify() 201 | 202 | fastify.get('/', function (request, reply) { 203 | reply.send({ hello: 'world' }) 204 | }) 205 | 206 | return fastify 207 | } 208 | 209 | module.exports = buildFastify 210 | ``` 211 | 212 | **test.js** 213 | ```js 214 | const tap = require('tap') 215 | const buildFastify = require('./app') 216 | 217 | tap.test('GET `/` route', t => { 218 | t.plan(4) 219 | 220 | const fastify = buildFastify() 221 | 222 | // At the end of your tests it is highly recommended to call `.close()` 223 | // to ensure that all connections to external services get closed. 224 | t.teardown(() => fastify.close()) 225 | 226 | fastify.inject({ 227 | method: 'GET', 228 | url: '/' 229 | }, (err, response) => { 230 | t.error(err) 231 | t.equal(response.statusCode, 200) 232 | t.equal(response.headers['content-type'], 'application/json; charset=utf-8') 233 | t.same(response.json(), { hello: 'world' }) 234 | }) 235 | }) 236 | ``` 237 | 238 | ### Testing with a running server 239 | Fastify can also be tested after starting the server with `fastify.listen()` or 240 | after initializing routes and plugins with `fastify.ready()`. 241 | 242 | #### Example: 243 | 244 | Uses **app.js** from the previous example. 245 | 246 | **test-listen.js** (testing with 247 | [`Request`](https://www.npmjs.com/package/request)) 248 | ```js 249 | const tap = require('tap') 250 | const request = require('request') 251 | const buildFastify = require('./app') 252 | 253 | tap.test('GET `/` route', t => { 254 | t.plan(5) 255 | 256 | const fastify = buildFastify() 257 | 258 | t.teardown(() => fastify.close()) 259 | 260 | fastify.listen({ port: 0 }, (err) => { 261 | t.error(err) 262 | 263 | request({ 264 | method: 'GET', 265 | url: 'http://localhost:' + fastify.server.address().port 266 | }, (err, response, body) => { 267 | t.error(err) 268 | t.equal(response.statusCode, 200) 269 | t.equal(response.headers['content-type'], 'application/json; charset=utf-8') 270 | t.same(JSON.parse(body), { hello: 'world' }) 271 | }) 272 | }) 273 | }) 274 | ``` 275 | 276 | **test-ready.js** (testing with 277 | [`SuperTest`](https://www.npmjs.com/package/supertest)) 278 | ```js 279 | const tap = require('tap') 280 | const supertest = require('supertest') 281 | const buildFastify = require('./app') 282 | 283 | tap.test('GET `/` route', async (t) => { 284 | const fastify = buildFastify() 285 | 286 | t.teardown(() => fastify.close()) 287 | 288 | await fastify.ready() 289 | 290 | const response = await supertest(fastify.server) 291 | .get('/') 292 | .expect(200) 293 | .expect('Content-Type', 'application/json; charset=utf-8') 294 | t.same(response.body, { hello: 'world' }) 295 | }) 296 | ``` 297 | 298 | ### How to inspect tap tests 299 | 1. Isolate your test by passing the `{only: true}` option 300 | ```javascript 301 | test('should ...', {only: true}, t => ...) 302 | ``` 303 | 2. Run `tap` using `npx` 304 | ```bash 305 | > npx tap -O -T --node-arg=--inspect-brk test/ 306 | ``` 307 | - `-O` specifies to run tests with the `only` option enabled 308 | - `-T` specifies not to timeout (while you're debugging) 309 | - `--node-arg=--inspect-brk` will launch the node debugger 310 | 3. In VS Code, create and launch a `Node.js: Attach` debug configuration. No 311 | modification should be necessary. 312 | 313 | Now you should be able to step through your test file (and the rest of 314 | `Fastify`) in your code editor. 315 | -------------------------------------------------------------------------------- /docs/Guides/Write-Plugin.md: -------------------------------------------------------------------------------- 1 |

Fastify

2 | 3 | # How to write a good plugin 4 | First, thank you for deciding to write a plugin for Fastify. Fastify is a 5 | minimal framework and plugins are its strength, so thank you. 6 | 7 | The core principles of Fastify are performance, low overhead, and providing a 8 | good experience to our users. When writing a plugin, it is important to keep 9 | these principles in mind. Therefore, in this document, we will analyze what 10 | characterizes a quality plugin. 11 | 12 | *Need some inspiration? You can use the label ["plugin 13 | suggestion"](https://github.com/fastify/fastify/issues?q=is%3Aissue+is%3Aopen+label%3A%22plugin+suggestion%22) 14 | in our issue tracker!* 15 | 16 | ## Code 17 | Fastify uses different techniques to optimize its code, many of them are 18 | documented in our Guides. We highly recommend you read [the hitchhiker's guide 19 | to plugins](./Plugins-Guide.md) to discover all the APIs you can use to build 20 | your plugin and learn how to use them. 21 | 22 | Do you have a question or need some advice? We are more than happy to help you! 23 | Just open an issue in our [help repository](https://github.com/fastify/help). 24 | 25 | Once you submit a plugin to our [ecosystem list](./Ecosystem.md), we will review 26 | your code and help you improve it if necessary. 27 | 28 | ## Documentation 29 | Documentation is extremely important. If your plugin is not well documented we 30 | will not accept it to the ecosystem list. Lack of quality documentation makes it 31 | more difficult for people to use your plugin, and will likely result in it going 32 | unused. 33 | 34 | If you want to see some good examples of how to document a plugin take a look 35 | at: 36 | - [`@fastify/caching`](https://github.com/fastify/fastify-caching) 37 | - [`@fastify/compress`](https://github.com/fastify/fastify-compress) 38 | - [`@fastify/cookie`](https://github.com/fastify/fastify-cookie) 39 | - [`@fastify/under-pressure`](https://github.com/fastify/under-pressure) 40 | - [`@fastify/view`](https://github.com/fastify/point-of-view) 41 | 42 | ## License 43 | You can license your plugin as you prefer, we do not enforce any kind of 44 | license. 45 | 46 | We prefer the [MIT license](https://choosealicense.com/licenses/mit/) because we 47 | think it allows more people to use the code freely. For a list of alternative 48 | licenses see the [OSI list](https://opensource.org/licenses) or GitHub's 49 | [choosealicense.com](https://choosealicense.com/). 50 | 51 | ## Examples 52 | Always put an example file in your repository. Examples are very helpful for 53 | users and give a very fast way to test your plugin. Your users will be grateful. 54 | 55 | ## Test 56 | It is extremely important that a plugin is thoroughly tested to verify that is 57 | working properly. 58 | 59 | A plugin without tests will not be accepted to the ecosystem list. A lack of 60 | tests does not inspire trust nor guarantee that the code will continue to work 61 | among different versions of its dependencies. 62 | 63 | We do not enforce any testing library. We use [`tap`](https://www.node-tap.org/) 64 | since it offers out-of-the-box parallel testing and code coverage, but it is up 65 | to you to choose your library of preference. 66 | 67 | ## Code Linter 68 | It is not mandatory, but we highly recommend you use a code linter in your 69 | plugin. It will ensure a consistent code style and help you to avoid many 70 | errors. 71 | 72 | We use [`standard`](https://standardjs.com/) since it works without the need to 73 | configure it and is very easy to integrate into a test suite. 74 | 75 | ## Continuous Integration 76 | It is not mandatory, but if you release your code as open source, it helps to 77 | use Continuous Integration to ensure contributions do not break your plugin and 78 | to show that the plugin works as intended. Both 79 | [CircleCI](https://circleci.com/) and [GitHub 80 | Actions](https://github.com/features/actions) are free for open source projects 81 | and easy to set up. 82 | 83 | In addition, you can enable services like [Dependabot](https://dependabot.com/), 84 | which will help you keep your dependencies up to date and discover if a new 85 | release of Fastify has some issues with your plugin. 86 | 87 | ## Let's start! 88 | Awesome, now you know everything you need to know about how to write a good 89 | plugin for Fastify! After you have built one (or more!) let us know! We will add 90 | it to the [ecosystem](https://github.com/fastify/fastify#ecosystem) section of 91 | our documentation! 92 | 93 | If you want to see some real world examples, check out: 94 | - [`@fastify/view`](https://github.com/fastify/point-of-view) Templates 95 | rendering (*ejs, pug, handlebars, marko*) plugin support for Fastify. 96 | - [`@fastify/mongodb`](https://github.com/fastify/fastify-mongodb) Fastify 97 | MongoDB connection plugin, with this you can share the same MongoDB connection 98 | pool in every part of your server. 99 | - [`@fastify/multipart`](https://github.com/fastify/fastify-multipart) Multipart 100 | support for Fastify. 101 | - [`@fastify/helmet`](https://github.com/fastify/fastify-helmet) Important 102 | security headers for Fastify. 103 | -------------------------------------------------------------------------------- /docs/Reference/ContentTypeParser.md: -------------------------------------------------------------------------------- 1 |

Fastify

2 | 3 | ## `Content-Type` 파서(Parser) 4 | 5 | 기본적으로 Fastify는 `'application/json'`과 `'text/plain'` 콘텐츠 타입만 지원합니다. 6 | 다른 컨텐츠 타입을 사용하면 `FST_ERR_CTP_INVALID_MEDIA_TYPE` 에러가 발생합니다. 7 | 8 | `utf-8` 인코딩이 기본값입니다. 다양한 컨텐츠 타입을 지원해야 하는 경우 `addContentTypeParser` API를 사용해야 합니다. _기본 JSON과 text 파서는 변경하거나 제거할 수 있습니다._ 9 | 10 | _참고: `Content-Type`헤더에 컨텐츠 타입을 지정한다면, UTF-8은 기본값이 아닙니다. `text/html; charset=utf-8`과 같이 UTF-8을 포함해야 합니다._ 11 | 12 | 다른 API와 마찬가지로 `addContentTypeParser`는 선언된 스코프에 캡슐화됩니다. 13 | 즉, 루트 범위에 선언하면 모든 곳에서 사용할 수 있는 반면, 플러그인 내부에 선언하면 해당 플러그인과 그 하위 스코프에서만 사용 가능합니다. 14 | 15 | Fastify는 파싱된 요청 페이로드를 [Fastify request](./Request.md) 객체에 자동으로 추가합니다. 이는 `request.body`로 접근할 수 있습니다. 16 | 17 | `GET`과 `HEAD` 요청의 페이로드는 절대 파싱되지 않는다는 것에 주의하세요. `OPTIONS`와 `DELETE` 요청의 페이로드는 콘텐츠 타입 헤더에 콘텐츠 타입이 주어질 때만 파싱됩니다. 콘텐츠 타입이 주어지지 않으면, [catch-all](#catch-all) 파서는 `POST`, `PUT`, `PATCH`에서처럼 실행되지 않습니다. 페이로드는 그저 파싱되지 않습니다. 18 | 19 | ### 사용법 20 | 21 | ```js 22 | fastify.addContentTypeParser( 23 | "application/jsoff", 24 | function (request, payload, done) { 25 | jsoffParser(payload, function (err, body) { 26 | done(err, body); 27 | }); 28 | } 29 | ); 30 | 31 | // 하나의 함수로 여러 Content-Type 처리하기 32 | fastify.addContentTypeParser( 33 | ["text/xml", "application/xml"], 34 | function (request, payload, done) { 35 | xmlParser(payload, function (err, body) { 36 | done(err, body); 37 | }); 38 | } 39 | ); 40 | 41 | // Node 버전 >= 8.0.0부터는 async도 지원됩니다 42 | fastify.addContentTypeParser( 43 | "application/jsoff", 44 | async function (request, payload) { 45 | var res = await jsoffParserAsync(payload); 46 | 47 | return res; 48 | } 49 | ); 50 | 51 | // 정규식에 부합하는 모든 콘텐츠 타입을 처리하기 52 | fastify.addContentTypeParser(/^image\/.*/, function (request, payload, done) { 53 | imageParser(payload, function (err, body) { 54 | done(err, body); 55 | }); 56 | }); 57 | 58 | // 기본적인 텍스트/JSON 파서를 다른 컨텐츠 타입에도 사용할 수 있습니다 59 | fastify.addContentTypeParser( 60 | "text/json", 61 | { parseAs: "string" }, 62 | fastify.getDefaultJsonParser("ignore", "ignore") 63 | ); 64 | ``` 65 | 66 | Fastify는 `RegExp`와 매칭되는 값을 찾기 전에 `string`으로 등록된 컨텐츠 타입 파서를 먼저 확인합니다. 겹치는 컨텐츠 타입을 작성하는 경우, Fastify는 마지막으로 주어진 컨텐츠 타입부터 사용하려고 할 것입니다. 따라서 범용 컨텐츠 타입을 보다 세부적으로 지정하려면, 아래의 예처럼 먼저 범용 컨텐츠 타입을 지정한 다음 구체적인 타입을 지정하세요. 67 | 68 | ```js 69 | // 여기에서는 첫 번째의 것도 매칭되기 때문에 두 번째 컨텐츠 타입 파서만 호출됩니다 70 | fastify.addContentTypeParser( 71 | "application/vnd.custom+xml", 72 | (request, body, done) => {} 73 | ); 74 | fastify.addContentTypeParser( 75 | "application/vnd.custom", 76 | (request, body, done) => {} 77 | ); 78 | 79 | //fastify가 `application/vnd.custom+xml`을 먼저 매칭하려고 하기 때문에 이것이 우리가 원했던 동작입니다 80 | fastify.addContentTypeParser( 81 | "application/vnd.custom", 82 | (request, body, done) => {} 83 | ); 84 | fastify.addContentTypeParser( 85 | "application/vnd.custom+xml", 86 | (request, body, done) => {} 87 | ); 88 | ``` 89 | 90 | `addContentTypeParser` API 뿐만 아니라 사용 가능한 더 많은 API들이 있습니다. 91 | `hasContentTypeParser`, `removeContentTypeParser`, 그리고 `removeAllContentTypeParsers`도 있습니다. 92 | 93 | #### hasContentTypeParser 94 | 95 | 특정한 컨텐츠 타입 파서가 있는지 확인하기 위해 `hasContentTypeParser`를 사용할 수 있습니다. 96 | 97 | ```js 98 | if (!fastify.hasContentTypeParser("application/jsoff")) { 99 | fastify.addContentTypeParser( 100 | "application/jsoff", 101 | function (request, payload, done) { 102 | jsoffParser(payload, function (err, body) { 103 | done(err, body); 104 | }); 105 | } 106 | ); 107 | } 108 | ``` 109 | 110 | ======= 111 | 112 | #### removeContentTypeParser 113 | 114 | `removeContentTypeParser`를 사용하면 하나의 혹은 배열로 된 컨텐츠 타입을 제거할 수 있습니다. 115 | 이 메서드는 `string`과 `RegExp`로 된 컨텐츠 타입 모두를 지원합니다. 116 | 117 | ```js 118 | fastify.addContentTypeParser("text/xml", function (request, payload, done) { 119 | xmlParser(payload, function (err, body) { 120 | done(err, body); 121 | }); 122 | }); 123 | 124 | // text/html만 사용 가능하도록 두 개의 내장된 컨텐츠 타입 파서를 제거합니다 125 | fastify.removeContentTypeParser(["application/json", "text/plain"]); 126 | ``` 127 | 128 | #### removeAllContentTypeParsers 129 | 130 | 위의 예제를 통해 제거하고자 하는 컨텐츠 타입을 모두 직접 지정해주어야 한다는 점을 알 수 있습니다. 131 | 이 문제를 해결하기 위해 Fastify는 `removeAllContentTypeParsers` API를 제공합니다. 132 | 이를 이용해 현재 존재하는 모든 컨텐츠 타입 파서를 제거할 수 있습니다. 133 | 아래 예제에서 우리는 제거하려는 컨텐츠 타입 파서를 직접 지정하지 않고도, 위 예제와 같은 동작을 구현할 수 있습니다. 134 | `removeContentTypeParser`와 마찬가지로 이 API도 캡슐화를 지원합니다. 135 | 내장 파서들을 무시하면서 모든 컨텐츠 타입에서 실행되어야 하는 [catch-all 컨텐츠 타입 파서](#Catch-All)를 등록하려는 경우 특히 유용합니다. 136 | 137 | ```js 138 | fastify.removeAllContentTypeParsers(); 139 | 140 | fastify.addContentTypeParser("text/xml", function (request, payload, done) { 141 | xmlParser(payload, function (err, body) { 142 | done(err, body); 143 | }); 144 | }); 145 | ``` 146 | 147 | **경고**: `function (req, done)`이나 `async function (req)`와 같은 오래된 문법들은 여전히 지원되지만 더 이상 사용되지는 않습니다. 148 | 149 | #### Body 파서 150 | 151 | 요청 바디는 2가지 방법으로 파싱할 수 있습니다. 첫 번째 방법은 위에 설명했듯, 사용자 정의 컨텐츠 타입 파서를 추가하고 요청 스트림을 처리하는 것입니다. 152 | 두 번째는 바디를 어떻게 가져올지 선언하는 `addContentTypeParser` API에 `parseAs` 옵션을 전달하는 것입니다. 153 | `parseAs` 옵션은 `'string'`이나 `'buffer'` 타입일 수 있습니다. 이 옵션을 사용하면 Fastify는 내부에서 스트림을 처리하고 바디의 [최대 크기](./Server.md#factory-body-limit)나 컨텐츠 길이와 같은 것을 검사합니다. 154 | 이러한 제한을 초과하면 사용자 정의 파서는 호출되지 않습니다. 155 | 156 | ```js 157 | fastify.addContentTypeParser( 158 | "application/json", 159 | { parseAs: "string" }, 160 | function (req, body, done) { 161 | try { 162 | var json = JSON.parse(body); 163 | done(null, json); 164 | } catch (err) { 165 | err.statusCode = 400; 166 | done(err, undefined); 167 | } 168 | } 169 | ); 170 | ``` 171 | 172 | 예시를 확인하려면 [`example/parser.js`](https://github.com/fastify/fastify/blob/main/examples/parser.js)를 참고하세요. 173 | 174 | ##### 커스텀 파서 옵션 175 | 176 | - `parseAs` (string): `'string'`과 `'buffer'` 모두 들어오는 데이터를 수집하는 방법을 지정합니다. 기본값은 `'buffer'`입니다. 177 | - `bodyLimit` (number): 커스텀 파서가 허용하는 최대 페이로드 크기(바이트)입니다. 기본값은 [`Fastify factory function`](./Server.md#bodylimit)에 전달된 글로벌 바디 크기로 제한합니다. 178 | 179 | #### Catch-All 180 | 181 | 컨텐츠 타입과 상관없이 모든 요청을 처리해야 하는 몇몇 상황들이 있습니다. 182 | Fastify를 사용하면 `'*'` 컨텐츠 타입을 지정하기만 하면 됩니다. 183 | 184 | ```js 185 | fastify.addContentTypeParser("*", function (request, payload, done) { 186 | var data = ""; 187 | payload.on("data", (chunk) => { 188 | data += chunk; 189 | }); 190 | payload.on("end", () => { 191 | done(null, data); 192 | }); 193 | }); 194 | ``` 195 | 196 | 이를 사용하면 해당 컨텐츠 타입 파서가 없는 모든 요청들은 지정된 함수에서 처리합니다. 197 | 198 | 또한 요청 스트림을 파이핑하는 데에도 유용합니다. 199 | 컨텐츠 파서를 다음과 같이 정의할 수 있습니다: 200 | 201 | ```js 202 | fastify.addContentTypeParser("*", function (request, payload, done) { 203 | done(); 204 | }); 205 | ``` 206 | 207 | 그런 다음 원하는 위치에 파이핑하기 위해 코어 HTTP 요청에 직접 접근합니다: 208 | 209 | ```js 210 | app.post("/hello", (request, reply) => { 211 | reply.send(request.raw); 212 | }); 213 | ``` 214 | 215 | 다음은 들어오는 [json line](https://jsonlines.org/) 객체를 로그하는 완전한 예제입니다: 216 | 217 | ```js 218 | const split2 = require("split2"); 219 | const pump = require("pump"); 220 | 221 | fastify.addContentTypeParser("*", (request, payload, done) => { 222 | done(null, pump(payload, split2(JSON.parse))); 223 | }); 224 | 225 | fastify.route({ 226 | method: "POST", 227 | url: "/api/log/jsons", 228 | handler: (req, res) => { 229 | req.body.on("data", (d) => console.log(d)); // 들어오는 모든 객체를 로그합니다 230 | }, 231 | }); 232 | ``` 233 | 234 | 파일 업로드를 파이핑할 때에는 [이 플러그인](https://github.com/fastify/fastify-multipart)에 관심이 있을 겁니다. 235 | 236 | 컨텐츠 타입 파서를 컨텐츠 타입이 없는 것들 뿐만 아니라 모든 컨텐츠 타입에서 실행하기를 원한다면, `removeAllContentTypeParsers`메서드를 먼저 호출해야 합니다. 237 | 238 | ```js 239 | // 이것을 실행하지 않는다면 application/json 타입의 요청 바디는 내장된 JSON 파서에 의해 처리됩니다. 240 | fastify.removeAllContentTypeParsers(); 241 | 242 | fastify.addContentTypeParser("*", function (request, payload, done) { 243 | var data = ""; 244 | payload.on("data", (chunk) => { 245 | data += chunk; 246 | }); 247 | payload.on("end", () => { 248 | done(null, data); 249 | }); 250 | }); 251 | ``` 252 | -------------------------------------------------------------------------------- /docs/Reference/Decorators.md: -------------------------------------------------------------------------------- 1 |

Fastify

2 | 3 | ## Decorators 4 | 5 | The decorators API allows customization of the core Fastify objects, such as the 6 | server instance itself and any request and reply objects used during the HTTP 7 | request lifecycle. The decorators API can be used to attach any type of property 8 | to the core objects, e.g. functions, plain objects, or native types. 9 | 10 | This API is *synchronous*. Attempting to define a decoration asynchronously 11 | could result in the Fastify instance booting before the decoration completes its 12 | initialization. To avoid this issue, and register an asynchronous decoration, 13 | the `register` API, in combination with `fastify-plugin`, must be used instead. 14 | To learn more, see the [Plugins](./Plugins.md) documentation. 15 | 16 | Decorating core objects with this API allows the underlying JavaScript engine to 17 | optimize the handling of server, request, and reply objects. This is 18 | accomplished by defining the shape of all such object instances before they are 19 | instantiated and used. As an example, the following is not recommended because 20 | it will change the shape of objects during their lifecycle: 21 | 22 | ```js 23 | // Bad example! Continue reading. 24 | 25 | // Attach a user property to the incoming request before the request 26 | // handler is invoked. 27 | fastify.addHook('preHandler', function (req, reply, done) { 28 | req.user = 'Bob Dylan' 29 | done() 30 | }) 31 | 32 | // Use the attached user property in the request handler. 33 | fastify.get('/', function (req, reply) { 34 | reply.send(`Hello, ${req.user}`) 35 | }) 36 | ``` 37 | 38 | Since the above example mutates the request object after it has already been 39 | instantiated, the JavaScript engine must deoptimize access to the request 40 | object. By using the decoration API this deoptimization is avoided: 41 | 42 | ```js 43 | // Decorate request with a 'user' property 44 | fastify.decorateRequest('user', '') 45 | 46 | // Update our property 47 | fastify.addHook('preHandler', (req, reply, done) => { 48 | req.user = 'Bob Dylan' 49 | done() 50 | }) 51 | // And finally access it 52 | fastify.get('/', (req, reply) => { 53 | reply.send(`Hello, ${req.user}!`) 54 | }) 55 | ``` 56 | 57 | Note that it is important to keep the initial shape of a decorated field as 58 | close as possible to the value intended to be set dynamically in the future. 59 | Initialize a decorator as a `''` if the intended value is a string, and as 60 | `null` if it will be an object or a function. 61 | 62 | Remember this example works only with value types as reference types will be 63 | shared amongst all requests. See [decorateRequest](#decorate-request). 64 | 65 | See [JavaScript engine fundamentals: Shapes and Inline 66 | Caches](https://mathiasbynens.be/notes/shapes-ics) for more information on this 67 | topic. 68 | 69 | ### Usage 70 | 71 | 72 | #### `decorate(name, value, [dependencies])` 73 | 74 | 75 | This method is used to customize the Fastify [server](./Server.md) 76 | instance. 77 | 78 | For example, to attach a new method to the server instance: 79 | 80 | ```js 81 | fastify.decorate('utility', function () { 82 | // Something very useful 83 | }) 84 | ``` 85 | 86 | As mentioned above, non-function values can be attached: 87 | 88 | ```js 89 | fastify.decorate('conf', { 90 | db: 'some.db', 91 | port: 3000 92 | }) 93 | ``` 94 | 95 | To access decorated properties, use the name provided to the decoration API: 96 | 97 | ```js 98 | fastify.utility() 99 | 100 | console.log(fastify.conf.db) 101 | ``` 102 | 103 | The decorated [Fastify server](./Server.md) is bound to `this` in 104 | route [route](./Routes.md) handlers: 105 | 106 | ```js 107 | fastify.decorate('db', new DbConnection()) 108 | 109 | fastify.get('/', async function (request, reply) { 110 | reply({hello: await this.db.query('world')}) 111 | }) 112 | ``` 113 | 114 | The `dependencies` parameter is an optional list of decorators that the 115 | decorator being defined relies upon. This list is simply a list of string names 116 | of other decorators. In the following example, the "utility" decorator depends 117 | upon "greet" and "log" decorators: 118 | 119 | ```js 120 | fastify.decorate('utility', fn, ['greet', 'log']) 121 | ``` 122 | 123 | Note: using an arrow function will break the binding of `this` to the 124 | `FastifyInstance`. 125 | 126 | If a dependency is not satisfied, the `decorate` method will throw an exception. 127 | The dependency check is performed before the server instance is booted. Thus, it 128 | cannot occur during runtime. 129 | 130 | #### `decorateReply(name, value, [dependencies])` 131 | 132 | 133 | As the name suggests, this API is used to add new methods/properties to the core 134 | `Reply` object: 135 | 136 | ```js 137 | fastify.decorateReply('utility', function () { 138 | // Something very useful 139 | }) 140 | ``` 141 | 142 | Note: using an arrow function will break the binding of `this` to the Fastify 143 | `Reply` instance. 144 | 145 | Note: using `decorateReply` will emit a warning if used with a reference type: 146 | 147 | ```js 148 | // Don't do this 149 | fastify.decorateReply('foo', { bar: 'fizz'}) 150 | ``` 151 | In this example, the reference of the object is shared with all the requests: 152 | **any mutation will impact all requests, potentially creating security 153 | vulnerabilities or memory leaks**. To achieve proper encapsulation across 154 | requests configure a new value for each incoming request in the [`'onRequest'` 155 | hook](./Hooks.md#onrequest). Example: 156 | 157 | ```js 158 | const fp = require('fastify-plugin') 159 | 160 | async function myPlugin (app) { 161 | app.decorateRequest('foo', null) 162 | app.addHook('onRequest', async (req, reply) => { 163 | req.foo = { bar: 42 } 164 | }) 165 | } 166 | 167 | module.exports = fp(myPlugin) 168 | ``` 169 | 170 | See [`decorate`](#decorate) for information about the `dependencies` parameter. 171 | 172 | #### `decorateRequest(name, value, [dependencies])` 173 | 174 | 175 | As above with [`decorateReply`](#decorate-reply), this API is used add new 176 | methods/properties to the core `Request` object: 177 | 178 | ```js 179 | fastify.decorateRequest('utility', function () { 180 | // something very useful 181 | }) 182 | ``` 183 | 184 | Note: using an arrow function will break the binding of `this` to the Fastify 185 | `Request` instance. 186 | 187 | Note: using `decorateRequest` will emit a warning if used with a reference type: 188 | 189 | ```js 190 | // Don't do this 191 | fastify.decorateRequest('foo', { bar: 'fizz'}) 192 | ``` 193 | In this example, the reference of the object is shared with all the requests: 194 | **any mutation will impact all requests, potentially creating security 195 | vulnerabilities or memory leaks**. 196 | 197 | To achieve proper encapsulation across requests configure a new value for each 198 | incoming request in the [`'onRequest'` hook](./Hooks.md#onrequest). Example: 199 | 200 | ```js 201 | const fp = require('fastify-plugin') 202 | 203 | async function myPlugin (app) { 204 | app.decorateRequest('foo', null) 205 | app.addHook('onRequest', async (req, reply) => { 206 | req.foo = { bar: 42 } 207 | }) 208 | } 209 | 210 | module.exports = fp(myPlugin) 211 | ``` 212 | 213 | See [`decorate`](#decorate) for information about the `dependencies` parameter. 214 | 215 | #### `hasDecorator(name)` 216 | 217 | 218 | Used to check for the existence of a server instance decoration: 219 | 220 | ```js 221 | fastify.hasDecorator('utility') 222 | ``` 223 | 224 | #### hasRequestDecorator 225 | 226 | 227 | Used to check for the existence of a Request decoration: 228 | 229 | ```js 230 | fastify.hasRequestDecorator('utility') 231 | ``` 232 | 233 | #### hasReplyDecorator 234 | 235 | 236 | Used to check for the existence of a Reply decoration: 237 | 238 | ```js 239 | fastify.hasReplyDecorator('utility') 240 | ``` 241 | 242 | ### Decorators and Encapsulation 243 | 244 | 245 | Defining a decorator (using `decorate`, `decorateRequest`, or `decorateReply`) 246 | with the same name more than once in the same **encapsulated** context will 247 | throw an exception. 248 | 249 | As an example, the following will throw: 250 | 251 | ```js 252 | const server = require('fastify')() 253 | 254 | server.decorateReply('view', function (template, args) { 255 | // Amazing view rendering engine 256 | }) 257 | 258 | server.get('/', (req, reply) => { 259 | reply.view('/index.html', { hello: 'world' }) 260 | }) 261 | 262 | // Somewhere else in our codebase, we define another 263 | // view decorator. This throws. 264 | server.decorateReply('view', function (template, args) { 265 | // Another rendering engine 266 | }) 267 | 268 | server.listen({ port: 3000 }) 269 | ``` 270 | 271 | 272 | But this will not: 273 | 274 | ```js 275 | const server = require('fastify')() 276 | 277 | server.decorateReply('view', function (template, args) { 278 | // Amazing view rendering engine. 279 | }) 280 | 281 | server.register(async function (server, opts) { 282 | // We add a view decorator to the current encapsulated 283 | // plugin. This will not throw as outside of this encapsulated 284 | // plugin view is the old one, while inside it is the new one. 285 | server.decorateReply('view', function (template, args) { 286 | // Another rendering engine 287 | }) 288 | 289 | server.get('/', (req, reply) => { 290 | reply.view('/index.page', { hello: 'world' }) 291 | }) 292 | }, { prefix: '/bar' }) 293 | 294 | server.listen({ port: 3000 }) 295 | ``` 296 | 297 | ### Getters and Setters 298 | 299 | 300 | Decorators accept special "getter/setter" objects. These objects have functions 301 | named `getter` and `setter` (though the `setter` function is optional). This 302 | allows defining properties via decorators, for example: 303 | 304 | ```js 305 | fastify.decorate('foo', { 306 | getter () { 307 | return 'a getter' 308 | } 309 | }) 310 | ``` 311 | 312 | Will define the `foo` property on the Fastify instance: 313 | 314 | ```js 315 | console.log(fastify.foo) // 'a getter' 316 | ``` 317 | -------------------------------------------------------------------------------- /docs/Reference/Encapsulation.md: -------------------------------------------------------------------------------- 1 |

Fastify

2 | 3 | ## Encapsulation 4 | 5 | 6 | A fundamental feature of Fastify is the "encapsulation context." The 7 | encapsulation context governs which [decorators](./Decorators.md), registered 8 | [hooks](./Hooks.md), and [plugins](./Plugins.md) are available to 9 | [routes](./Routes.md). A visual representation of the encapsulation context 10 | is shown in the following figure: 11 | 12 | ![Figure 1](../resources/encapsulation_context.svg) 13 | 14 | In the above figure, there are several entities: 15 | 16 | 1. The _root context_ 17 | 2. Three _root plugins_ 18 | 3. Two _child contexts_ where each _child context_ has 19 | * Two _child plugins_ 20 | * One _grandchild context_ where each _grandchild context_ has 21 | - Three _child plugins_ 22 | 23 | Every _child context_ and _grandchild context_ has access to the _root plugins_. 24 | Within each _child context_, the _grandchild contexts_ have access to the 25 | _child plugins_ registered within the containing _child context_, but the 26 | containing _child context_ **does not** have access to the _child plugins_ 27 | registered within its _grandchild context_. 28 | 29 | Given that everything in Fastify is a [plugin](./Plugins.md), except for the 30 | _root context_, every "context" and "plugin" in this example is a plugin 31 | that can consist of decorators, hooks, plugins, and routes. Thus, to put 32 | this example into concrete terms, consider a basic scenario of a REST API 33 | server that has three routes: the first route (`/one`) requires authentication, 34 | the second route (`/two`) does not, and the third route (`/three`) has 35 | access to the same context as the second route. Using 36 | [@fastify/bearer-auth][bearer] to provide the authentication, the code for this 37 | example is as follows: 38 | 39 | ```js 40 | 'use strict' 41 | 42 | const fastify = require('fastify')() 43 | 44 | fastify.decorateRequest('answer', 42) 45 | 46 | fastify.register(async function authenticatedContext (childServer) { 47 | childServer.register(require('@fastify/bearer-auth'), { keys: ['abc123'] }) 48 | 49 | childServer.route({ 50 | path: '/one', 51 | method: 'GET', 52 | handler (request, response) { 53 | response.send({ 54 | answer: request.answer, 55 | // request.foo will be undefined as it's only defined in publicContext 56 | foo: request.foo, 57 | // request.bar will be undefined as it's only defined in grandchildContext 58 | bar: request.bar 59 | }) 60 | } 61 | }) 62 | }) 63 | 64 | fastify.register(async function publicContext (childServer) { 65 | childServer.decorateRequest('foo', 'foo') 66 | 67 | childServer.route({ 68 | path: '/two', 69 | method: 'GET', 70 | handler (request, response) { 71 | response.send({ 72 | answer: request.answer, 73 | foo: request.foo, 74 | // request.bar will be undefined as it's only defined in grandchildContext 75 | bar: request.bar 76 | }) 77 | } 78 | }) 79 | 80 | childServer.register(async function grandchildContext (grandchildServer) { 81 | grandchildServer.decorateRequest('bar', 'bar') 82 | 83 | grandchildServer.route({ 84 | path: '/three', 85 | method: 'GET', 86 | handler (request, response) { 87 | response.send({ 88 | answer: request.answer, 89 | foo: request.foo, 90 | bar: request.bar 91 | }) 92 | } 93 | }) 94 | }) 95 | }) 96 | 97 | fastify.listen({ port: 8000 }) 98 | ``` 99 | 100 | The above server example shows all of the encapsulation concepts outlined in the 101 | original diagram: 102 | 103 | 1. Each _child context_ (`authenticatedContext`, `publicContext`, and 104 | `grandchildContext`) has access to the `answer` request decorator defined in 105 | the _root context_. 106 | 2. Only the `authenticatedContext` has access to the `@fastify/bearer-auth` 107 | plugin. 108 | 3. Both the `publicContext` and `grandchildContext` have access to the `foo` 109 | request decorator. 110 | 4. Only the `grandchildContext` has access to the `bar` request decorator. 111 | 112 | To see this, start the server and issue requests: 113 | 114 | ```sh 115 | # curl -H 'authorization: Bearer abc123' http://127.0.0.1:8000/one 116 | {"answer":42} 117 | # curl http://127.0.0.1:8000/two 118 | {"answer":42,"foo":"foo"} 119 | # curl http://127.0.0.1:8000/three 120 | {"answer":42,"foo":"foo","bar":"bar"} 121 | ``` 122 | 123 | [bearer]: https://github.com/fastify/fastify-bearer-auth 124 | 125 | ## Sharing Between Contexts 126 | 127 | 128 | Notice that each context in the prior example inherits _only_ from the parent 129 | contexts. Parent contexts cannot access any entities within their descendent 130 | contexts. This default is occasionally not desired. In such cases, the 131 | encapsulation context can be broken through the usage of 132 | [fastify-plugin][fastify-plugin] such that anything registered in a descendent 133 | context is available to the containing parent context. 134 | 135 | Assuming the `publicContext` needs access to the `bar` decorator defined 136 | within the `grandchildContext` in the previous example, the code can be 137 | rewritten as: 138 | 139 | ```js 140 | 'use strict' 141 | 142 | const fastify = require('fastify')() 143 | const fastifyPlugin = require('fastify-plugin') 144 | 145 | fastify.decorateRequest('answer', 42) 146 | 147 | // `authenticatedContext` omitted for clarity 148 | 149 | fastify.register(async function publicContext (childServer) { 150 | childServer.decorateRequest('foo', 'foo') 151 | 152 | childServer.route({ 153 | path: '/two', 154 | method: 'GET', 155 | handler (request, response) { 156 | response.send({ 157 | answer: request.answer, 158 | foo: request.foo, 159 | bar: request.bar 160 | }) 161 | } 162 | }) 163 | 164 | childServer.register(fastifyPlugin(grandchildContext)) 165 | 166 | async function grandchildContext (grandchildServer) { 167 | grandchildServer.decorateRequest('bar', 'bar') 168 | 169 | grandchildServer.route({ 170 | path: '/three', 171 | method: 'GET', 172 | handler (request, response) { 173 | response.send({ 174 | answer: request.answer, 175 | foo: request.foo, 176 | bar: request.bar 177 | }) 178 | } 179 | }) 180 | } 181 | }) 182 | 183 | fastify.listen({ port: 8000 }) 184 | ``` 185 | 186 | Restarting the server and re-issuing the requests for `/two` and `/three`: 187 | 188 | ```sh 189 | # curl http://127.0.0.1:8000/two 190 | {"answer":42,"foo":"foo","bar":"bar"} 191 | # curl http://127.0.0.1:8000/three 192 | {"answer":42,"foo":"foo","bar":"bar"} 193 | ``` 194 | 195 | [fastify-plugin]: https://github.com/fastify/fastify-plugin 196 | -------------------------------------------------------------------------------- /docs/Reference/Errors.md: -------------------------------------------------------------------------------- 1 |

Fastify

2 | 3 | ## Errors 4 | 5 | 6 | ### Error Handling In Node.js 7 | 8 | 9 | #### Uncaught Errors 10 | In Node.js, uncaught errors are likely to cause memory leaks, file descriptor 11 | leaks, and other major production issues. 12 | [Domains](https://nodejs.org/en/docs/guides/domain-postmortem/) were a failed 13 | attempt to fix this. 14 | 15 | Given that it is not possible to process all uncaught errors sensibly, the best 16 | way to deal with them is to 17 | [crash](https://nodejs.org/api/process.html#process_warning_using_uncaughtexception_correctly). 18 | 19 | #### Catching Errors In Promises 20 | If you are using promises, you should attach a `.catch()` handler synchronously. 21 | 22 | ### Errors In Fastify 23 | Fastify follows an all-or-nothing approach and aims to be lean and optimal as 24 | much as possible. The developer is responsible for making sure that the errors 25 | are handled properly. 26 | 27 | #### Errors In Input Data 28 | Most errors are a result of unexpected input data, so we recommend [validating 29 | your input data against a JSON schema](./Validation-and-Serialization.md). 30 | 31 | #### Catching Uncaught Errors In Fastify 32 | Fastify tries to catch as many uncaught errors as it can without hindering 33 | performance. This includes: 34 | 35 | 1. synchronous routes, e.g. `app.get('/', () => { throw new Error('kaboom') })` 36 | 2. `async` routes, e.g. `app.get('/', async () => { throw new Error('kaboom') 37 | })` 38 | 39 | The error in both cases will be caught safely and routed to Fastify's default 40 | error handler for a generic `500 Internal Server Error` response. 41 | 42 | To customize this behavior you should use 43 | [`setErrorHandler`](./Server.md#seterrorhandler). 44 | 45 | ### Errors In Fastify Lifecycle Hooks And A Custom Error Handler 46 | 47 | From the [Hooks documentation](./Hooks.md#manage-errors-from-a-hook): 48 | > If you get an error during the execution of your hook, just pass it to 49 | > `done()` and Fastify will automatically close the request and send the 50 | > appropriate error code to the user. 51 | 52 | When a custom error handler has been defined through 53 | [`setErrorHandler`](./Server.md#seterrorhandler), the custom error handler will 54 | receive the error passed to the `done()` callback (or through other supported 55 | automatic error handling mechanisms). If `setErrorHandler` has been used 56 | multiple times to define multiple handlers, the error will be routed to the most 57 | precedent handler defined within the error [encapsulation 58 | context](./Encapsulation.md). Error handlers are fully encapsulated, so a 59 | `setErrorHandler` call within a plugin will limit the error handler to that 60 | plugin's context. 61 | 62 | The root error handler is Fastify's generic error handler. This error handler 63 | will use the headers and status code in the `Error` object, if they exist. The 64 | headers and status code will not be automatically set if a custom error handler 65 | is provided. 66 | 67 | Some things to consider in your custom error handler: 68 | 69 | - you can `reply.send(data)`, which will behave as it would in [regular route 70 | handlers](./Reply.md#senddata) 71 | - objects are serialized, triggering the `preSerialization` lifecycle hook if 72 | you have one defined 73 | - strings, buffers, and streams are sent to the client, with appropriate 74 | headers (no serialization) 75 | 76 | - You can throw a new error in your custom error handler - errors (new error or 77 | the received error parameter re-thrown) - will call the parent `errorHandler`. 78 | - `onError` hook will be triggered once only for the first error being thrown. 79 | - an error will not be triggered twice from a lifecycle hook - Fastify 80 | internally monitors the error invocation to avoid infinite loops for errors 81 | thrown in the reply phases of the lifecycle. (those after the route handler) 82 | 83 | ### Fastify Error Codes 84 | 85 | 86 | #### FST_ERR_BAD_URL 87 | 88 | 89 | The router received an invalid url. 90 | 91 | 92 | #### FST_ERR_DUPLICATED_ROUTE 93 | 94 | The HTTP method already has a registered controller for that URL 95 | 96 | 97 | #### FST_ERR_CTP_ALREADY_PRESENT 98 | 99 | 100 | The parser for this content type was already registered. 101 | 102 | #### FST_ERR_CTP_BODY_TOO_LARGE 103 | 104 | 105 | The request body is larger than the provided limit. 106 | 107 | This setting can be defined in the Fastify server instance: 108 | [`bodyLimit`](./Server.md#bodylimit) 109 | 110 | #### FST_ERR_CTP_EMPTY_TYPE 111 | 112 | 113 | The content type cannot be an empty string. 114 | 115 | #### FST_ERR_CTP_INVALID_CONTENT_LENGTH 116 | 117 | 118 | Request body size did not match Content-Length. 119 | 120 | #### FST_ERR_CTP_INVALID_HANDLER 121 | 122 | 123 | An invalid handler was passed for the content type. 124 | 125 | #### FST_ERR_CTP_INVALID_MEDIA_TYPE 126 | 127 | 128 | The received media type is not supported (i.e. there is no suitable 129 | `Content-Type` parser for it). 130 | 131 | #### FST_ERR_CTP_INVALID_PARSE_TYPE 132 | 133 | 134 | The provided parse type is not supported. Accepted values are `string` or 135 | `buffer`. 136 | 137 | #### FST_ERR_CTP_INVALID_TYPE 138 | 139 | 140 | The `Content-Type` should be a string. 141 | 142 | #### FST_ERR_DEC_ALREADY_PRESENT 143 | 144 | 145 | A decorator with the same name is already registered. 146 | 147 | #### FST_ERR_DEC_MISSING_DEPENDENCY 148 | 149 | 150 | The decorator cannot be registered due to a missing dependency. 151 | 152 | #### FST_ERR_HOOK_INVALID_HANDLER 153 | 154 | 155 | The hook callback must be a function. 156 | 157 | #### FST_ERR_HOOK_INVALID_TYPE 158 | 159 | 160 | The hook name must be a string. 161 | 162 | #### FST_ERR_LOG_INVALID_DESTINATION 163 | 164 | 165 | The logger accepts either a `'stream'` or a `'file'` as the destination. 166 | 167 | #### FST_ERR_PROMISE_NOT_FULFILLED 168 | 169 | 170 | A promise may not be fulfilled with 'undefined' when statusCode is not 204. 171 | 172 | #### FST_ERR_REP_ALREADY_SENT 173 | 174 | 175 | A response was already sent. 176 | 177 | #### FST_ERR_REP_INVALID_PAYLOAD_TYPE 178 | 179 | 180 | Reply payload can be either a `string` or a `Buffer`. 181 | 182 | #### FST_ERR_SCH_ALREADY_PRESENT 183 | 184 | 185 | A schema with the same `$id` already exists. 186 | 187 | #### FST_ERR_SCH_MISSING_ID 188 | 189 | 190 | The schema provided does not have `$id` property. 191 | 192 | #### FST_ERR_SCH_SERIALIZATION_BUILD 193 | 194 | 195 | The JSON schema provided for serialization of a route response is not valid. 196 | 197 | #### FST_ERR_SCH_VALIDATION_BUILD 198 | 199 | 200 | The JSON schema provided for validation to a route is not valid. 201 | 202 | #### FST_ERR_SEND_INSIDE_ONERR 203 | 204 | 205 | You cannot use `send` inside the `onError` hook. 206 | 207 | #### FST_ERR_SEND_UNDEFINED_ERR 208 | 209 | 210 | Undefined error has occurred. 211 | 212 | 213 | #### FST_ERR_PLUGIN_NOT_VALID 214 | 215 | Plugin must be a function or a promise. 216 | 217 | 218 | #### FST_ERR_PLUGIN_TIMEOUT 219 | 220 | Plugin did not start in time. Default timeout (in millis): `10000` 221 | 222 | 223 | #### FST_ERR_HOOK_TIMEOUT 224 | 225 | A callback for a hook timed out 226 | 227 | 228 | #### FST_ERR_ROOT_PLG_BOOTED 229 | 230 | Root plugin has already booted (mapped directly from `avvio`) 231 | 232 | 233 | #### FST_ERR_PARENT_PLUGIN_BOOTED 234 | 235 | Impossible to load plugin because the parent (mapped directly from `avvio`) 236 | 237 | 238 | #### FST_ERR_PLUGIN_CALLBACK_NOT_FN 239 | 240 | Callback for a hook is not a function (mapped directly from `avvio`) 241 | -------------------------------------------------------------------------------- /docs/Reference/HTTP2.md: -------------------------------------------------------------------------------- 1 |

Fastify

2 | 3 | ## HTTP2 4 | 5 | _Fastify_ offers **experimental support** for HTTP2 starting from Node 8 LTS, 6 | which includes HTTP2 without a flag; HTTP2 is supported over either HTTPS or 7 | plaintext. 8 | 9 | Currently, none of the HTTP2-specific APIs are available through _Fastify_, but 10 | Node's `req` and `res` can be accessed through our `Request` and `Reply` 11 | interface. PRs are welcome. 12 | 13 | ### Secure (HTTPS) 14 | 15 | HTTP2 is supported in all modern browsers __only over a secure connection__: 16 | 17 | ```js 18 | 'use strict' 19 | 20 | const fs = require('fs') 21 | const path = require('path') 22 | const fastify = require('fastify')({ 23 | http2: true, 24 | https: { 25 | key: fs.readFileSync(path.join(__dirname, '..', 'https', 'fastify.key')), 26 | cert: fs.readFileSync(path.join(__dirname, '..', 'https', 'fastify.cert')) 27 | } 28 | }) 29 | 30 | fastify.get('/', function (request, reply) { 31 | reply.code(200).send({ hello: 'world' }) 32 | }) 33 | 34 | fastify.listen({ port: 3000 }) 35 | ``` 36 | 37 | ALPN negotiation allows support for both HTTPS and HTTP/2 over the same socket. 38 | Node core `req` and `res` objects can be either 39 | [HTTP/1](https://nodejs.org/api/http.html) or 40 | [HTTP/2](https://nodejs.org/api/http2.html). _Fastify_ supports this out of the 41 | box: 42 | 43 | ```js 44 | 'use strict' 45 | 46 | const fs = require('fs') 47 | const path = require('path') 48 | const fastify = require('fastify')({ 49 | http2: true, 50 | https: { 51 | allowHTTP1: true, // fallback support for HTTP1 52 | key: fs.readFileSync(path.join(__dirname, '..', 'https', 'fastify.key')), 53 | cert: fs.readFileSync(path.join(__dirname, '..', 'https', 'fastify.cert')) 54 | } 55 | }) 56 | 57 | // this route can be accessed through both protocols 58 | fastify.get('/', function (request, reply) { 59 | reply.code(200).send({ hello: 'world' }) 60 | }) 61 | 62 | fastify.listen({ port: 3000 }) 63 | ``` 64 | 65 | You can test your new server with: 66 | 67 | ``` 68 | $ npx h2url https://localhost:3000 69 | ``` 70 | 71 | ### Plain or insecure 72 | 73 | If you are building microservices, you can connect to HTTP2 in plain text, 74 | however, this is not supported by browsers. 75 | 76 | ```js 77 | 'use strict' 78 | 79 | const fastify = require('fastify')({ 80 | http2: true 81 | }) 82 | 83 | fastify.get('/', function (request, reply) { 84 | reply.code(200).send({ hello: 'world' }) 85 | }) 86 | 87 | fastify.listen({ port: 3000 }) 88 | ``` 89 | 90 | You can test your new server with: 91 | 92 | ``` 93 | $ npx h2url http://localhost:3000 94 | ``` 95 | 96 | -------------------------------------------------------------------------------- /docs/Reference/Index.md: -------------------------------------------------------------------------------- 1 |

Fastify

2 | 3 | ## Core Documents 4 | 5 | 6 | For the full table of contents (TOC), see [below](#reference-toc). The following 7 | list is a subset of the full TOC that detail core Fastify APIs and concepts in 8 | order of most likely importance to the reader: 9 | 10 | + [Server](./Server.md): Documents the core Fastify API. Includes documentation 11 | for the factory function and the object returned by the factory function. 12 | + [Lifecycle](./Lifecycle.md): Explains the Fastify request lifecycle and 13 | illustrates where [Hooks](./Hooks.md) are available for integrating with it. 14 | + [Routes](./Routes.md): Details how to register routes with Fastify and how 15 | Fastify builds and evaluates the routing trie. 16 | + [Request](./Request.md): Details Fastify's request object that is passed into 17 | each request handler. 18 | + [Reply](./Reply.md): Details Fastify's response object available to each 19 | request handler. 20 | + [Validation and Serialization](./Validation-and-Serialization.md): Details 21 | Fastify's support for validating incoming data and how Fastify serializes data 22 | for responses. 23 | + [Plugins](./Plugins.md): Explains Fastify's plugin architecture and API. 24 | + [Encapsulation](./Encapsulation.md): Explains a core concept upon which all 25 | Fastify plugins are built. 26 | + [Decorators](./Decorators.md): Explains the server, request, and response 27 | decorator APIs. 28 | + [Hooks](./Hooks.md): Details the API by which Fastify plugins can inject 29 | themselves into Fastify's handling of the request lifecycle. 30 | 31 | 32 | ## Reference Documentation Table Of Contents 33 | 34 | 35 | This table of contents is in alphabetical order. 36 | 37 | + [Content Type Parser](./ContentTypeParser.md): Documents Fastify's default 38 | content type parser and how to add support for new content types. 39 | + [Decorators](./Decorators.md): Explains the server, request, and response 40 | decorator APIs. 41 | + [Encapsulation](./Encapsulation.md): Explains a core concept upon which all 42 | Fastify plugins are built. 43 | + [Errors](./Errors.md): Details how Fastify handles errors and lists the 44 | standard set of errors Fastify generates. 45 | + [Hooks](./Hooks.md): Details the API by which Fastify plugins can inject 46 | themselves into Fastify's handling of the request lifecycle. 47 | + [HTTP2](./HTTP2.md): Details Fastify's HTTP2 support. 48 | + [Lifecycle](./Lifecycle.md): Explains the Fastify request lifecycle and 49 | illustrates where [Hooks](./Hooks.md) are available for integrating with it. 50 | + [Logging](./Logging.md): Details Fastify's included logging and how to 51 | customize it. 52 | + [Long Term Support](./LTS.md): Explains Fastify's long term support (LTS) 53 | guarantee and the exceptions possible to the [semver](https://semver.org) 54 | contract. 55 | + [Middleware](./Middleware.md): Details Fastify's support for Express.js style 56 | middleware. 57 | + [Plugins](./Plugins.md): Explains Fastify's plugin architecture and API. 58 | + [Reply](./Reply.md): Details Fastify's response object available to each 59 | request handler. 60 | + [Request](./Request.md): Details Fastify's request object that is passed into 61 | each request handler. 62 | + [Routes](./Routes.md): Details how to register routes with Fastify and how 63 | Fastify builds and evaluates the routing trie. 64 | + [Server](./Server.md): Documents the core Fastify API. Includes documentation 65 | for the factory function and the object returned by the factory function. 66 | + [TypeScript](./TypeScript.md): Documents Fastify's TypeScript support and 67 | provides recommendations for writing applications in TypeScript that utilize 68 | Fastify. 69 | + [Validation and Serialization](./Validation-and-Serialization.md): Details 70 | Fastify's support for validating incoming data and how Fastify serializes data 71 | for responses. 72 | -------------------------------------------------------------------------------- /docs/Reference/LTS.md: -------------------------------------------------------------------------------- 1 |

Fastify

2 | 3 | ## Long Term Support 4 | 5 | 6 | Fastify's Long Term Support (LTS) is provided according to the schedule laid out 7 | in this document: 8 | 9 | 1. Major releases, "X" release of [semantic versioning][semver] X.Y.Z release 10 | versions, are supported for a minimum period of six months from their release 11 | date. The release date of any specific version can be found at 12 | [https://github.com/fastify/fastify/releases](https://github.com/fastify/fastify/releases). 13 | 14 | 2. Major releases will receive security updates for an additional six months 15 | from the release of the next major release. After this period we will still 16 | review and release security fixes as long as they are provided by the 17 | community and they do not violate other constraints, e.g. minimum supported 18 | Node.js version. 19 | 20 | 3. Major releases will be tested and verified against all Node.js release lines 21 | that are supported by the [Node.js LTS 22 | policy](https://github.com/nodejs/Release) within the LTS period of that 23 | given Fastify release line. This implies that only the latest Node.js release 24 | of a given line is supported. 25 | 26 | A "month" is defined as 30 consecutive days. 27 | 28 | > ## Security Releases and Semver 29 | > 30 | > As a consequence of providing long-term support for major releases, there are 31 | > occasions where we need to release breaking changes as a _minor_ version 32 | > release. Such changes will _always_ be noted in the [release 33 | > notes](https://github.com/fastify/fastify/releases). 34 | > 35 | > To avoid automatically receiving breaking security updates it is possible to 36 | > use the tilde (`~`) range qualifier. For example, to get patches for the 3.15 37 | > release, and avoid automatically updating to the 3.16 release, specify the 38 | > dependency as `"fastify": "~3.15.x"`. This will leave your application 39 | > vulnerable, so please use with caution. 40 | 41 | [semver]: https://semver.org/ 42 | 43 | ### Schedule 44 | 45 | 46 | | Version | Release Date | End Of LTS Date | Node.js | 47 | | :------ | :----------- | :-------------- | :------------------- | 48 | | 1.0.0 | 2018-03-06 | 2019-09-01 | 6, 8, 9, 10, 11 | 49 | | 2.0.0 | 2019-02-25 | 2021-01-31 | 6, 8, 10, 12, 14 | 50 | | 3.0.0 | 2020-07-07 | 2023-06-30 | 10, 12, 14, 16, 18 | 51 | | 4.0.0 | 2022-06-08 | TBD | 14, 16, 18 | 52 | 53 | ### CI tested operating systems 54 | 55 | 56 | Fastify uses GitHub Actions for CI testing, please refer to [GitHub's 57 | documentation regarding workflow 58 | runners](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources) 59 | for further details on what the latest virtual environment is in relation to the 60 | YAML workflow labels below: 61 | 62 | | OS | YAML Workflow Label | Package Manager | Node.js | 63 | |---------|------------------------|---------------------------|--------------| 64 | | Linux | `ubuntu-latest` | npm | 14,16,18 | 65 | | Linux | `ubuntu-18.04` | yarn,pnpm | 14,16,18 | 66 | | Windows | `windows-latest` | npm | 14,16,18 | 67 | | MacOS | `macos-latest` | npm | 14,16,18 | 68 | 69 | Using [yarn](https://yarnpkg.com/) might require passing the `--ignore-engines` 70 | flag. 71 | -------------------------------------------------------------------------------- /docs/Reference/Lifecycle.md: -------------------------------------------------------------------------------- 1 |

Fastify

2 | 3 | ## Lifecycle 4 | Following the schema of the internal lifecycle of Fastify. 5 | 6 | On the right branch of every section there is the next phase of the lifecycle, 7 | on the left branch there is the corresponding error code that will be generated 8 | if the parent throws an error *(note that all the errors are automatically 9 | handled by Fastify)*. 10 | 11 | ``` 12 | Incoming Request 13 | │ 14 | └─▶ Routing 15 | │ 16 | └─▶ Instance Logger 17 | │ 18 | 4**/5** ◀─┴─▶ onRequest Hook 19 | │ 20 | 4**/5** ◀─┴─▶ preParsing Hook 21 | │ 22 | 4**/5** ◀─┴─▶ Parsing 23 | │ 24 | 4**/5** ◀─┴─▶ preValidation Hook 25 | │ 26 | 400 ◀─┴─▶ Validation 27 | │ 28 | 4**/5** ◀─┴─▶ preHandler Hook 29 | │ 30 | 4**/5** ◀─┴─▶ User Handler 31 | │ 32 | └─▶ Reply 33 | │ 34 | 4**/5** ◀─┴─▶ preSerialization Hook 35 | │ 36 | └─▶ onSend Hook 37 | │ 38 | 4**/5** ◀─┴─▶ Outgoing Response 39 | │ 40 | └─▶ onResponse Hook 41 | ``` 42 | 43 | At any point before or during the `User Handler`, `reply.hijack()` can be called 44 | to prevent Fastify from: 45 | - Running all the following hooks and user handler 46 | - Sending the response automatically 47 | 48 | NB (*): If `reply.raw` is used to send a response back to the user, `onResponse` 49 | hooks will still be executed 50 | 51 | ## Reply Lifecycle 52 | 53 | Whenever the user handles the request, the result may be: 54 | 55 | - in async handler: it returns a payload 56 | - in async handler: it throws an `Error` 57 | - in sync handler: it sends a payload 58 | - in sync handler: it sends an `Error` instance 59 | 60 | If the reply was hijacked, we skip all the below steps. Otherwise, when it is 61 | being submitted, the data flow performed is the following: 62 | 63 | ``` 64 | ★ schema validation Error 65 | │ 66 | └─▶ schemaErrorFormatter 67 | │ 68 | reply sent ◀── JSON ─┴─ Error instance 69 | │ 70 | │ ★ throw an Error 71 | ★ send or return │ │ 72 | │ │ │ 73 | │ ▼ │ 74 | reply sent ◀── JSON ─┴─ Error instance ──▶ setErrorHandler ◀─────┘ 75 | │ 76 | reply sent ◀── JSON ─┴─ Error instance ──▶ onError Hook 77 | │ 78 | └─▶ reply sent 79 | ``` 80 | 81 | Note: `reply sent` means that the JSON payload will be serialized by: 82 | 83 | - the [reply serialized](./Server.md#setreplyserializer) if set 84 | - or by the [serializer compiler](./Server.md#setserializercompiler) when a JSON 85 | schema has been set for the returning HTTP status code 86 | - or by the default `JSON.stringify` function 87 | -------------------------------------------------------------------------------- /docs/Reference/Logging.md: -------------------------------------------------------------------------------- 1 |

Fastify

2 | 3 | ## Logging 4 | 5 | ### Enable logging 6 | Logging is disabled by default, and you can enable it by passing `{ logger: true 7 | }` or `{ logger: { level: 'info' } }` when you create a Fastify instance. Note 8 | that if the logger is disabled, it is impossible to enable it at runtime. We use 9 | [abstract-logging](https://www.npmjs.com/package/abstract-logging) for this 10 | purpose. 11 | 12 | As Fastify is focused on performance, it uses 13 | [pino](https://github.com/pinojs/pino) as its logger, with the default log 14 | level, when enabled, set to `'info'`. 15 | 16 | Enabling the production JSON logger: 17 | 18 | ```js 19 | const fastify = require('fastify')({ 20 | logger: true 21 | }) 22 | ``` 23 | 24 | Enabling the logger with appropriate configuration for both local development 25 | and production environment requires bit more configuration: 26 | ```js 27 | const fastify = require('fastify')({ 28 | logger: { 29 | transport: 30 | environment === 'development' 31 | ? { 32 | target: 'pino-pretty', 33 | options: { 34 | translateTime: 'HH:MM:ss Z', 35 | ignore: 'pid,hostname' 36 | } 37 | } 38 | : undefined 39 | } 40 | }) 41 | ``` 42 | ⚠️ `pino-pretty` needs to be installed as a dev dependency, it is not included 43 | by default for performance reasons. 44 | 45 | ### Usage 46 | You can use the logger like this in your route handlers: 47 | 48 | ```js 49 | fastify.get('/', options, function (request, reply) { 50 | request.log.info('Some info about the current request') 51 | reply.send({ hello: 'world' }) 52 | }) 53 | ``` 54 | 55 | You can trigger new logs outside route handlers by using the Pino instance from 56 | the Fastify instance: 57 | ```js 58 | fastify.log.info('Something important happened!'); 59 | ``` 60 | 61 | If you want to pass some options to the logger, just pass them to Fastify. You 62 | can find all available options in the [Pino 63 | documentation](https://github.com/pinojs/pino/blob/master/docs/api.md#pinooptions-stream). 64 | If you want to specify a file destination, use: 65 | 66 | ```js 67 | const fastify = require('fastify')({ 68 | logger: { 69 | level: 'info', 70 | file: '/path/to/file' // Will use pino.destination() 71 | } 72 | }) 73 | 74 | fastify.get('/', options, function (request, reply) { 75 | request.log.info('Some info about the current request') 76 | reply.send({ hello: 'world' }) 77 | }) 78 | ``` 79 | 80 | If you want to pass a custom stream to the Pino instance, just add a stream 81 | field to the logger object. 82 | 83 | ```js 84 | const split = require('split2') 85 | const stream = split(JSON.parse) 86 | 87 | const fastify = require('fastify')({ 88 | logger: { 89 | level: 'info', 90 | stream: stream 91 | } 92 | }) 93 | ``` 94 | 95 | 96 | 97 | By default, Fastify adds an ID to every request for easier tracking. If the 98 | "request-id" header is present its value is used, otherwise a new incremental ID 99 | is generated. See Fastify Factory 100 | [`requestIdHeader`](./Server.md#factory-request-id-header) and Fastify Factory 101 | [`genReqId`](./Server.md#genreqid) for customization options. 102 | 103 | The default logger is configured with a set of standard serializers that 104 | serialize objects with `req`, `res`, and `err` properties. The object received 105 | by `req` is the Fastify [`Request`](./Request.md) object, while the object 106 | received by `res` is the Fastify [`Reply`](./Reply.md) object. This behaviour 107 | can be customized by specifying custom serializers. 108 | ```js 109 | const fastify = require('fastify')({ 110 | logger: { 111 | serializers: { 112 | req (request) { 113 | return { url: request.url } 114 | } 115 | } 116 | } 117 | }) 118 | ``` 119 | For example, the response payload and headers could be logged using the approach 120 | below (even if it is *not recommended*): 121 | 122 | ```js 123 | const fastify = require('fastify')({ 124 | logger: { 125 | transport: { 126 | target: 'pino-pretty' 127 | }, 128 | serializers: { 129 | res (reply) { 130 | // The default 131 | return { 132 | statusCode: reply.statusCode 133 | } 134 | }, 135 | req (request) { 136 | return { 137 | method: request.method, 138 | url: request.url, 139 | path: request.routerPath, 140 | parameters: request.params, 141 | // Including the headers in the log could be in violation 142 | // of privacy laws, e.g. GDPR. You should use the "redact" option to 143 | // remove sensitive fields. It could also leak authentication data in 144 | // the logs. 145 | headers: request.headers 146 | }; 147 | } 148 | } 149 | } 150 | }); 151 | ``` 152 | **Note**: The body cannot be serialized inside a `req` method because the 153 | request is serialized when we create the child logger. At that time, the body is 154 | not yet parsed. 155 | 156 | See an approach to log `req.body` 157 | 158 | ```js 159 | app.addHook('preHandler', function (req, reply, done) { 160 | if (req.body) { 161 | req.log.info({ body: req.body }, 'parsed body') 162 | } 163 | done() 164 | }) 165 | ``` 166 | 167 | 168 | *Any logger other than Pino will ignore this option.* 169 | 170 | You can also supply your own logger instance. Instead of passing configuration 171 | options, pass the instance. The logger you supply must conform to the Pino 172 | interface; that is, it must have the following methods: `info`, `error`, 173 | `debug`, `fatal`, `warn`, `trace`, `child`. 174 | 175 | Example: 176 | 177 | ```js 178 | const log = require('pino')({ level: 'info' }) 179 | const fastify = require('fastify')({ logger: log }) 180 | 181 | log.info('does not have request information') 182 | 183 | fastify.get('/', function (request, reply) { 184 | request.log.info('includes request information, but is the same logger instance as `log`') 185 | reply.send({ hello: 'world' }) 186 | }) 187 | ``` 188 | 189 | *The logger instance for the current request is available in every part of the 190 | [lifecycle](./Lifecycle.md).* 191 | 192 | ## Log Redaction 193 | 194 | [Pino](https://getpino.io) supports low-overhead log redaction for obscuring 195 | values of specific properties in recorded logs. As an example, we might want to 196 | log all the HTTP headers minus the `Authorization` header for security concerns: 197 | 198 | ```js 199 | const fastify = Fastify({ 200 | logger: { 201 | stream: stream, 202 | redact: ['req.headers.authorization'], 203 | level: 'info', 204 | serializers: { 205 | req (request) { 206 | return { 207 | method: request.method, 208 | url: request.url, 209 | headers: request.headers, 210 | hostname: request.hostname, 211 | remoteAddress: request.ip, 212 | remotePort: request.socket.remotePort 213 | } 214 | } 215 | } 216 | } 217 | }) 218 | ``` 219 | 220 | See https://getpino.io/#/docs/redaction for more details. 221 | -------------------------------------------------------------------------------- /docs/Reference/Middleware.md: -------------------------------------------------------------------------------- 1 |

Fastify

2 | 3 | ## Middleware 4 | 5 | Starting with Fastify v3.0.0, middleware is not supported out of the box and 6 | requires an external plugin such as 7 | [`@fastify/express`](https://github.com/fastify/fastify-express) or 8 | [`@fastify/middie`](https://github.com/fastify/middie). 9 | 10 | 11 | An example of registering the 12 | [`@fastify/express`](https://github.com/fastify/fastify-express) plugin to `use` 13 | Express middleware: 14 | 15 | ```js 16 | await fastify.register(require('@fastify/express')) 17 | fastify.use(require('cors')()) 18 | fastify.use(require('dns-prefetch-control')()) 19 | fastify.use(require('frameguard')()) 20 | fastify.use(require('hsts')()) 21 | fastify.use(require('ienoopen')()) 22 | fastify.use(require('x-xss-protection')()) 23 | ``` 24 | 25 | You can also use [`@fastify/middie`](https://github.com/fastify/middie), which provides 26 | support for simple Express-style middleware but with improved performance: 27 | 28 | ```js 29 | await fastify.register(require('@fastify/middie')) 30 | fastify.use(require('cors')()) 31 | ``` 32 | 33 | Remember that middleware can be encapsulated; this means that you can decide 34 | where your middleware should run by using `register` as explained in the 35 | [plugins guide](../Guides/Plugins-Guide.md). 36 | 37 | Fastify middleware does not expose the `send` method or other methods specific to 38 | the Fastify [Reply](./Reply.md#reply) instance. This is because Fastify wraps 39 | the incoming `req` and `res` Node instances using the 40 | [Request](./Request.md#request) and [Reply](./Reply.md#reply) objects 41 | internally, but this is done after the middleware phase. If you need to create 42 | middleware, you have to use the Node `req` and `res` instances. Otherwise, you 43 | can use the `preHandler` hook that already has the 44 | [Request](./Request.md#request) and [Reply](./Reply.md#reply) Fastify instances. 45 | For more information, see [Hooks](./Hooks.md#hooks). 46 | 47 | #### Restrict middleware execution to certain paths 48 | 49 | 50 | If you need to only run middleware under certain paths, just pass the path as 51 | the first parameter to `use` and you are done! 52 | 53 | *Note that this does not support routes with parameters, (e.g. 54 | `/user/:id/comments`) and wildcards are not supported in multiple paths.* 55 | 56 | ```js 57 | const path = require('path') 58 | const serveStatic = require('serve-static') 59 | 60 | // Single path 61 | fastify.use('/css', serveStatic(path.join(__dirname, '/assets'))) 62 | 63 | // Wildcard path 64 | fastify.use('/css/(.*)', serveStatic(path.join(__dirname, '/assets'))) 65 | 66 | // Multiple paths 67 | fastify.use(['/css', '/js'], serveStatic(path.join(__dirname, '/assets'))) 68 | ``` 69 | 70 | ### Alternatives 71 | 72 | Fastify offers some alternatives to the most commonly used middleware, such as 73 | [`@fastify/helmet`](https://github.com/fastify/fastify-helmet) in case of 74 | [`helmet`](https://github.com/helmetjs/helmet), 75 | [`@fastify/cors`](https://github.com/fastify/fastify-cors) for 76 | [`cors`](https://github.com/expressjs/cors), and 77 | [`@fastify/static`](https://github.com/fastify/fastify-static) for 78 | [`serve-static`](https://github.com/expressjs/serve-static). 79 | -------------------------------------------------------------------------------- /docs/Reference/Plugins.md: -------------------------------------------------------------------------------- 1 |

Fastify

2 | 3 | ## Plugins 4 | Fastify allows the user to extend its functionalities with plugins. A plugin can 5 | be a set of routes, a server [decorator](./Decorators.md), or whatever. The API 6 | that you will need to use one or more plugins, is `register`. 7 | 8 | By default, `register` creates a *new scope*, this means that if you make some 9 | changes to the Fastify instance (via `decorate`), this change will not be 10 | reflected by the current context ancestors, but only by its descendants. This 11 | feature allows us to achieve plugin *encapsulation* and *inheritance*, in this 12 | way we create a *directed acyclic graph* (DAG) and we will not have issues 13 | caused by cross dependencies. 14 | 15 | You may have already seen in the [Getting 16 | Started](../Guides/Getting-Started.md#your-first-plugin) guide how easy it is 17 | to use this API: 18 | ``` 19 | fastify.register(plugin, [options]) 20 | ``` 21 | 22 | ### Plugin Options 23 | 24 | 25 | The optional `options` parameter for `fastify.register` supports a predefined 26 | set of options that Fastify itself will use, except when the plugin has been 27 | wrapped with [fastify-plugin](https://github.com/fastify/fastify-plugin). This 28 | options object will also be passed to the plugin upon invocation, regardless of 29 | whether or not the plugin has been wrapped. The currently supported list of 30 | Fastify specific options is: 31 | 32 | + [`logLevel`](./Routes.md#custom-log-level) 33 | + [`logSerializers`](./Routes.md#custom-log-serializer) 34 | + [`prefix`](#route-prefixing-option) 35 | 36 | **Note: Those options will be ignored when used with fastify-plugin** 37 | 38 | It is possible that Fastify will directly support other options in the future. 39 | Thus, to avoid collisions, a plugin should consider namespacing its options. For 40 | example, a plugin `foo` might be registered like so: 41 | 42 | ```js 43 | fastify.register(require('fastify-foo'), { 44 | prefix: '/foo', 45 | foo: { 46 | fooOption1: 'value', 47 | fooOption2: 'value' 48 | } 49 | }) 50 | ``` 51 | 52 | If collisions are not a concern, the plugin may simply accept the options object 53 | as-is: 54 | 55 | ```js 56 | fastify.register(require('fastify-foo'), { 57 | prefix: '/foo', 58 | fooOption1: 'value', 59 | fooOption2: 'value' 60 | }) 61 | ``` 62 | 63 | The `options` parameter can also be a `Function` that will be evaluated at the 64 | time the plugin is registered while giving access to the Fastify instance via 65 | the first positional argument: 66 | 67 | ```js 68 | const fp = require('fastify-plugin') 69 | 70 | fastify.register(fp((fastify, opts, done) => { 71 | fastify.decorate('foo_bar', { hello: 'world' }) 72 | 73 | done() 74 | })) 75 | 76 | // The opts argument of fastify-foo will be { hello: 'world' } 77 | fastify.register(require('fastify-foo'), parent => parent.foo_bar) 78 | ``` 79 | 80 | The Fastify instance passed on to the function is the latest state of the 81 | **external Fastify instance** the plugin was declared on, allowing access to 82 | variables injected via [`decorate`](./Decorators.md) by preceding plugins 83 | according to the **order of registration**. This is useful in case a plugin 84 | depends on changes made to the Fastify instance by a preceding plugin i.e. 85 | utilizing an existing database connection to wrap around it. 86 | 87 | Keep in mind that the Fastify instance passed on to the function is the same as 88 | the one that will be passed into the plugin, a copy of the external Fastify 89 | instance rather than a reference. Any usage of the instance will behave the same 90 | as it would if called within the plugins function i.e. if `decorate` is called, 91 | the decorated variables will be available within the plugins function unless it 92 | was wrapped with [`fastify-plugin`](https://github.com/fastify/fastify-plugin). 93 | 94 | #### Route Prefixing option 95 | 96 | 97 | If you pass an option with the key `prefix` with a `string` value, Fastify will 98 | use it to prefix all the routes inside the register, for more info check 99 | [here](./Routes.md#route-prefixing). 100 | 101 | Be aware that if you wrap your routes with 102 | [`fastify-plugin`](https://github.com/fastify/fastify-plugin), this option will 103 | not work (there is a [workaround](./Routes.md#fastify-plugin) available). 104 | 105 | #### Error handling 106 | 107 | 108 | The error handling is done by 109 | [avvio](https://github.com/mcollina/avvio#error-handling). 110 | 111 | As a general rule, it is highly recommended that you handle your errors in the 112 | next `after` or `ready` block, otherwise you will get them inside the `listen` 113 | callback. 114 | 115 | ```js 116 | fastify.register(require('my-plugin')) 117 | 118 | // `after` will be executed once 119 | // the previous declared `register` has finished 120 | fastify.after(err => console.log(err)) 121 | 122 | // `ready` will be executed once all the registers declared 123 | // have finished their execution 124 | fastify.ready(err => console.log(err)) 125 | 126 | // `listen` is a special ready, 127 | // so it behaves in the same way 128 | fastify.listen({ port: 3000 }, (err, address) => { 129 | if (err) console.log(err) 130 | }) 131 | ``` 132 | 133 | ### async/await 134 | 135 | 136 | *async/await* is supported by `after`, `ready`, and `listen`, as well as 137 | `fastify` being a [Thenable](https://promisesaplus.com/). 138 | 139 | ```js 140 | await fastify.register(require('my-plugin')) 141 | 142 | await fastify.after() 143 | 144 | await fastify.ready() 145 | 146 | await fastify.listen({ port: 3000 }) 147 | ``` 148 | 149 | #### ESM support 150 | 151 | 152 | ESM is supported as well from [Node.js 153 | `v13.3.0`](https://nodejs.org/api/esm.html) and above! 154 | 155 | ```js 156 | // main.mjs 157 | import Fastify from 'fastify' 158 | const fastify = Fastify() 159 | 160 | fastify.register(import('./plugin.mjs')) 161 | 162 | fastify.listen({ port: 3000 }, console.log) 163 | 164 | 165 | // plugin.mjs 166 | async function plugin (fastify, opts) { 167 | fastify.get('/', async (req, reply) => { 168 | return { hello: 'world' } 169 | }) 170 | } 171 | 172 | export default plugin 173 | ``` 174 | 175 | ### Create a plugin 176 | 177 | 178 | Creating a plugin is very easy, you just need to create a function that takes 179 | three parameters, the `fastify` instance, an `options` object, and the `done` 180 | callback. 181 | 182 | Example: 183 | ```js 184 | module.exports = function (fastify, opts, done) { 185 | fastify.decorate('utility', function () {}) 186 | 187 | fastify.get('/', handler) 188 | 189 | done() 190 | } 191 | ``` 192 | You can also use `register` inside another `register`: 193 | ```js 194 | module.exports = function (fastify, opts, done) { 195 | fastify.decorate('utility', function () {}) 196 | 197 | fastify.get('/', handler) 198 | 199 | fastify.register(require('./other-plugin')) 200 | 201 | done() 202 | } 203 | ``` 204 | Sometimes, you will need to know when the server is about to close, for example, 205 | because you must close a connection to a database. To know when this is going to 206 | happen, you can use the [`'onClose'`](./Hooks.md#on-close) hook. 207 | 208 | Do not forget that `register` will always create a new Fastify scope, if you do 209 | not need that, read the following section. 210 | 211 | ### Handle the scope 212 | 213 | 214 | If you are using `register` only for extending the functionality of the server 215 | with [`decorate`](./Decorators.md), it is your responsibility to tell Fastify 216 | not to create a new scope. Otherwise, your changes will not be accessible by the 217 | user in the upper scope. 218 | 219 | You have two ways to tell Fastify to avoid the creation of a new context: 220 | - Use the [`fastify-plugin`](https://github.com/fastify/fastify-plugin) module 221 | - Use the `'skip-override'` hidden property 222 | 223 | We recommend using the `fastify-plugin` module, because it solves this problem 224 | for you, and you can pass a version range of Fastify as a parameter that your 225 | plugin will support. 226 | ```js 227 | const fp = require('fastify-plugin') 228 | 229 | module.exports = fp(function (fastify, opts, done) { 230 | fastify.decorate('utility', function () {}) 231 | done() 232 | }, '0.x') 233 | ``` 234 | Check the [`fastify-plugin`](https://github.com/fastify/fastify-plugin) 235 | documentation to learn more about how to use this module. 236 | 237 | If you do not use the `fastify-plugin` module, you can use the `'skip-override'` 238 | hidden property, but we do not recommend it. If in the future the Fastify API 239 | changes it will be your responsibility to update the module, while if you use 240 | `fastify-plugin`, you can be sure about backward compatibility. 241 | ```js 242 | function yourPlugin (fastify, opts, done) { 243 | fastify.decorate('utility', function () {}) 244 | done() 245 | } 246 | yourPlugin[Symbol.for('skip-override')] = true 247 | module.exports = yourPlugin 248 | ``` 249 | -------------------------------------------------------------------------------- /docs/Reference/Request.md: -------------------------------------------------------------------------------- 1 |

Fastify

2 | 3 | ## Request 4 | The first parameter of the handler function is `Request`. 5 | 6 | Request is a core Fastify object containing the following fields: 7 | - `query` - the parsed querystring, its format is specified by 8 | [`querystringParser`](./Server.md#querystringparser) 9 | - `body` - the request payload, see [Content-Type 10 | Parser](./ContentTypeParser.md) for details on what request payloads Fastify 11 | natively parses and how to support other content types 12 | - `params` - the params matching the URL 13 | - [`headers`](#headers) - the headers getter and setter 14 | - `raw` - the incoming HTTP request from Node core 15 | - `server` - The Fastify server instance, scoped to the current [encapsulation 16 | context](./Encapsulation.md) 17 | - `id` - the request ID 18 | - `log` - the logger instance of the incoming request 19 | - `ip` - the IP address of the incoming request 20 | - `ips` - an array of the IP addresses, ordered from closest to furthest, in the 21 | `X-Forwarded-For` header of the incoming request (only when the 22 | [`trustProxy`](./Server.md#factory-trust-proxy) option is enabled) 23 | - `hostname` - the host of the incoming request (derived from `X-Forwarded-Host` 24 | header when the [`trustProxy`](./Server.md#factory-trust-proxy) option is 25 | enabled). For HTTP/2 compatibility it returns `:authority` if no host header 26 | exists. 27 | - `protocol` - the protocol of the incoming request (`https` or `http`) 28 | - `method` - the method of the incoming request 29 | - `url` - the URL of the incoming request 30 | - `routerMethod` - the method defined for the router that is handling the 31 | request 32 | - `routerPath` - the path pattern defined for the router that is handling the 33 | request 34 | - `is404` - true if request is being handled by 404 handler, false if it is not 35 | - `connection` - Deprecated, use `socket` instead. The underlying connection of 36 | the incoming request. 37 | - `socket` - the underlying connection of the incoming request 38 | - `context` - A Fastify internal object. You should not use it directly or 39 | modify it. It is useful to access one special key: 40 | - `context.config` - The route [`config`](./Routes.md#routes-config) object. 41 | 42 | ### Headers 43 | 44 | The `request.headers` is a getter that returns an Object with the headers of the 45 | incoming request. You can set custom headers like this: 46 | 47 | ```js 48 | request.headers = { 49 | 'foo': 'bar', 50 | 'baz': 'qux' 51 | } 52 | ``` 53 | 54 | This operation will add to the request headers the new values that can be read 55 | calling `request.headers.bar`. Moreover, you can still access the standard 56 | request's headers with the `request.raw.headers` property. 57 | 58 | > Note: For performance reason on `not found` route, you may see that we will 59 | add an extra property `Symbol('fastify.RequestAcceptVersion')` on the headers. 60 | 61 | ```js 62 | fastify.post('/:params', options, function (request, reply) { 63 | console.log(request.body) 64 | console.log(request.query) 65 | console.log(request.params) 66 | console.log(request.headers) 67 | console.log(request.raw) 68 | console.log(request.server) 69 | console.log(request.id) 70 | console.log(request.ip) 71 | console.log(request.ips) 72 | console.log(request.hostname) 73 | console.log(request.protocol) 74 | console.log(request.url) 75 | console.log(request.routerMethod) 76 | console.log(request.routerPath) 77 | request.log.info('some info') 78 | }) 79 | ``` 80 | -------------------------------------------------------------------------------- /docs/Reference/Type-Providers.md: -------------------------------------------------------------------------------- 1 |

Fastify

2 | 3 | ## Type Providers 4 | 5 | Type Providers are a TypeScript only feature that enables Fastify to statically 6 | infer type information directly from inline JSON Schema. They are an alternative 7 | to specifying generic arguments on routes; and can greatly reduce the need to 8 | keep associated types for each schema defined in your project. 9 | 10 | ### Providers 11 | 12 | Type Providers are offered as additional packages you will need to install into 13 | your project. Each provider uses a different inference library under the hood; 14 | allowing you to select the library most appropriate for your needs. Type 15 | Provider packages follow a `@fastify/type-provider-{provider-name}` naming 16 | convention. 17 | 18 | The following inference packages are supported: 19 | 20 | - `json-schema-to-ts` - 21 | [github](https://github.com/ThomasAribart/json-schema-to-ts) 22 | - `typebox` - [github](https://github.com/sinclairzx81/typebox) 23 | 24 | ### Json Schema to Ts 25 | 26 | The following sets up a `json-schema-to-ts` Type Provider 27 | 28 | ```bash 29 | $ npm i @fastify/type-provider-json-schema-to-ts 30 | ``` 31 | 32 | ```typescript 33 | import { JsonSchemaToTsProvider } from '@fastify/type-provider-json-schema-to-ts' 34 | 35 | import fastify from 'fastify' 36 | 37 | const server = fastify().withTypeProvider() 38 | 39 | server.get('/route', { 40 | schema: { 41 | querystring: { 42 | type: 'object', 43 | properties: { 44 | foo: { type: 'number' }, 45 | bar: { type: 'string' }, 46 | }, 47 | required: ['foo', 'bar'] 48 | } 49 | } as const // don't forget to use const ! 50 | 51 | }, (request, reply) => { 52 | 53 | // type Query = { foo: number, bar: string } 54 | 55 | const { foo, bar } = request.query // type safe! 56 | }) 57 | ``` 58 | 59 | ### TypeBox 60 | 61 | The following sets up a TypeBox Type Provider 62 | 63 | ```bash 64 | $ npm i @fastify/type-provider-typebox 65 | ``` 66 | 67 | ```typescript 68 | import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox' 69 | import { Type } from '@sinclair/typebox' 70 | 71 | import fastify from 'fastify' 72 | 73 | const server = fastify().withTypeProvider() 74 | 75 | server.get('/route', { 76 | schema: { 77 | querystring: Type.Object({ 78 | foo: Type.Number(), 79 | bar: Type.String() 80 | }) 81 | } 82 | }, (request, reply) => { 83 | 84 | // type Query = { foo: number, bar: string } 85 | 86 | const { foo, bar } = request.query // type safe! 87 | }) 88 | ``` 89 | 90 | See also the [TypeBox 91 | documentation](https://github.com/sinclairzx81/typebox#validation) on how to set 92 | up AJV to work with TypeBox. 93 | 94 | ### Scoped Type-Provider 95 | 96 | The provider types don't propagate globally. In encapsulated usage, one can 97 | remap the context to use one or more providers (for example, `typebox` and 98 | `json-schema-to-ts` can be used in the same application). 99 | 100 | Example: 101 | 102 | ```ts 103 | import Fastify from 'fastify' 104 | import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox' 105 | import { JsonSchemaToTsProvider } from '@fastify/type-provider-json-schema-to-ts' 106 | import { Type } from '@sinclair/typebox' 107 | 108 | const fastify = Fastify() 109 | 110 | function pluginWithTypebox(fastify: FastifyInstance, _opts, done): void { 111 | fastify.withTypeProvider() 112 | .get('/', { 113 | schema: { 114 | body: Type.Object({ 115 | x: Type.String(), 116 | y: Type.Number(), 117 | z: Type.Boolean() 118 | }) 119 | } 120 | }, (req) => { 121 | const { x, y, z } = req.body // type safe 122 | }); 123 | done() 124 | } 125 | 126 | function pluginWithJsonSchema(fastify: FastifyInstance, _opts, done): void { 127 | fastify.withTypeProvider() 128 | .get('/', { 129 | schema: { 130 | body: { 131 | type: 'object', 132 | properties: { 133 | x: { type: 'string' }, 134 | y: { type: 'number' }, 135 | z: { type: 'boolean' } 136 | }, 137 | } as const 138 | } 139 | }, (req) => { 140 | const { x, y, z } = req.body // type safe 141 | }); 142 | done() 143 | } 144 | 145 | fastify.register(pluginWithJsonSchema) 146 | fastify.register(pluginWithTypebox) 147 | ``` 148 | 149 | It's also important to mention that once the types don't propagate globally, 150 | _currently_ is not possible to avoid multiple registrations on routes when 151 | dealing with several scopes, see bellow: 152 | 153 | ```ts 154 | import Fastify from 'fastify' 155 | import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox' 156 | import { Type } from '@sinclair/typebox' 157 | 158 | const server = Fastify({ 159 | ajv: { 160 | customOptions: { 161 | strict: 'log', 162 | keywords: ['kind', 'modifier'], 163 | }, 164 | }, 165 | }).withTypeProvider() 166 | 167 | server.register(plugin1) // wrong 168 | server.register(plugin2) // correct 169 | 170 | function plugin1(fastify: FastifyInstance, _opts, done): void { 171 | fastify.get('/', { 172 | schema: { 173 | body: Type.Object({ 174 | x: Type.String(), 175 | y: Type.Number(), 176 | z: Type.Boolean() 177 | }) 178 | } 179 | }, (req) => { 180 | // it doesn't works! in a new scope needs to call `withTypeProvider` again 181 | const { x, y, z } = req.body 182 | }); 183 | done() 184 | } 185 | 186 | function plugin2(fastify: FastifyInstance, _opts, done): void { 187 | const server = fastify.withTypeProvider() 188 | 189 | server.get('/', { 190 | schema: { 191 | body: Type.Object({ 192 | x: Type.String(), 193 | y: Type.Number(), 194 | z: Type.Boolean() 195 | }) 196 | } 197 | }, (req) => { 198 | // works 199 | const { x, y, z } = req.body 200 | }); 201 | done() 202 | } 203 | ``` 204 | 205 | ### Type Definition of FastifyInstance + TypeProvider 206 | 207 | When working with modules one has to make use of `FastifyInstance` with Type 208 | Provider generics. See the example below: 209 | 210 | ```ts 211 | // index.ts 212 | import Fastify from 'fastify' 213 | import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox' 214 | import { registerRoutes } from './routes' 215 | 216 | const server = Fastify({ 217 | ajv: { 218 | customOptions: { 219 | strict: 'log', 220 | keywords: ['kind', 'modifier'], 221 | }, 222 | }, 223 | }).withTypeProvider() 224 | 225 | registerRoutes(server) 226 | 227 | server.listen({ port: 3000 }) 228 | ``` 229 | 230 | ```ts 231 | // routes.ts 232 | import { Type } from '@sinclair/typebox' 233 | import { 234 | FastifyInstance, 235 | FastifyLoggerInstance, 236 | RawReplyDefaultExpression, 237 | RawRequestDefaultExpression, 238 | RawServerDefault 239 | } from 'fastify' 240 | import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox' 241 | 242 | type FastifyTypebox = FastifyInstance< 243 | RawServerDefault, 244 | RawRequestDefaultExpression, 245 | RawReplyDefaultExpression, 246 | FastifyLoggerInstance, 247 | TypeBoxTypeProvider 248 | >; 249 | 250 | export function registerRoutes(fastify: FastifyTypebox): void { 251 | fastify.get('/', { 252 | schema: { 253 | body: Type.Object({ 254 | x: Type.String(), 255 | y: Type.Number(), 256 | z: Type.Boolean() 257 | }) 258 | } 259 | }, (req) => { 260 | // works 261 | const { x, y, z } = req.body 262 | }); 263 | } 264 | ``` 265 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 |

Fastify

2 | 3 | Fastify 공식 문서는 두 종류로 나뉩니다. 4 | 5 | - 참조 문서 [Reference documentation](./Reference/Index.md) 6 | - 가이드 [Guides](./Guides/Index.md) 7 | 8 | 참조 문서는 공식적인 스타일로 Fastify API와 세부 구현 사항을 철저히 기술하여 이를 필요로 하는 개발자에게 제공합니다. 9 | 10 | 가이드는 비공식적이고 교육적인 스타일로 입문자를 위한 Fastify의 핵심, 심화 컨셉을 다룹니다. 11 | 12 | ## 시작하기 13 | 14 | Fastify를 처음 사용하는 사람은 먼저 [시작하기](./Guides/Getting-Started.md) 가이드를 읽어야 합니다. 15 | 16 | Fastify를 사용한 경험이 있는 개발자는 바로 [참조 문서](./Reference/Index.md)를 이용해 원하는 주제를 찾을 수 있습니다. 17 | 18 | ## 추가 문서 19 | 20 | - Fastify의 [Long Term Support (LTS)](./Reference/LTS.md) 정책 21 | --------------------------------------------------------------------------------