├── .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 | 
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 |
--------------------------------------------------------------------------------