Compare commits

..

17 Commits

Author SHA1 Message Date
Sebastián Ramírez
4b83b0d409 🔖 Release version 0.138.0 (#15808)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-20 01:14:30 +00:00
github-actions[bot]
041cb0cdfa 📝 Update release notes
[skip ci]
2026-06-20 01:07:09 +00:00
Sebastián Ramírez
10393846ed 📝 Fix typo in release notes (#15807) 2026-06-20 01:06:46 +00:00
github-actions[bot]
0303491b69 📝 Update release notes
[skip ci]
2026-06-20 00:59:37 +00:00
Sebastián Ramírez
190f6e2033 📝 Add Frontend instructions to Agent Library Skill (#15805) 2026-06-20 00:59:14 +00:00
github-actions[bot]
17945e5ab7 📝 Update release notes
[skip ci]
2026-06-20 00:51:57 +00:00
Sebastián Ramírez
2260afaf43 🐛 Fix failing test, update format for raised errors (#15804) 2026-06-20 00:51:31 +00:00
github-actions[bot]
0cd5001d0e 📝 Update release notes
[skip ci]
2026-06-20 00:45:46 +00:00
Sebastián Ramírez
7cb1ab6264 👷 Fix test-alls-green (#15803) 2026-06-20 02:45:19 +02:00
github-actions[bot]
9c7eceb00f 📝 Update release notes
[skip ci]
2026-06-20 00:30:21 +00:00
Sebastián Ramírez
d176e00b9f 📝 Udpate release notes link (#15802) 2026-06-20 00:29:58 +00:00
github-actions[bot]
71e608e00e 📝 Update release notes
[skip ci]
2026-06-20 00:26:30 +00:00
Sebastián Ramírez
459a51097b ✏️ Update white space characters in bigger apps (#15801) 2026-06-20 00:26:04 +00:00
github-actions[bot]
e12833aaa2 📝 Update release notes
[skip ci]
2026-06-20 00:21:10 +00:00
Sebastián Ramírez
4d3dc78b26 Add support for app.frontend("/", directory="dist") and router.frontend("/", directory="dist") (#15800) 2026-06-20 00:20:49 +00:00
github-actions[bot]
f1d750fdda 📝 Update release notes
[skip ci]
2026-06-19 22:28:49 +00:00
Yurii Motov
22f99d9ad3 🔧 Enable checking release-notes.md for typos (#15796) 2026-06-20 00:28:24 +02:00
31 changed files with 1712 additions and 2656 deletions

View File

@@ -245,9 +245,10 @@ jobs:
- run: uv run coverage report --fail-under=100
# https://github.com/marketplace/actions/alls-green#why
check: # This job does nothing and is only used for the branch protection
test-alls-green: # This job does nothing and is only used for the branch protection
if: always()
needs:
- test
- coverage-combine
- benchmark
runs-on: ubuntu-latest

View File

@@ -13,6 +13,7 @@ from fastapi import APIRouter
members:
- websocket
- include_router
- frontend
- get
- put
- post

View File

@@ -18,6 +18,7 @@ from fastapi import FastAPI
- openapi
- websocket
- include_router
- frontend
- get
- put
- post

View File

@@ -7,8 +7,19 @@ hide:
## Latest Changes
## 0.138.0 (2026-06-20)
### Features
* ✨ Add support for `app.frontend("/", directory="dist")` and `router.frontend("/", directory="dist")`. PR [#15800](https://github.com/fastapi/fastapi/pull/15800) by [@tiangolo](https://github.com/tiangolo).
* Read the docs: [Frontend](https://fastapi.tiangolo.com/tutorial/frontend/).
### Docs
* 📝 Fix typo in release notes. PR [#15807](https://github.com/fastapi/fastapi/pull/15807) by [@tiangolo](https://github.com/tiangolo).
* 📝 Add `app.frontend()` instructions to Agent Library Skill. PR [#15805](https://github.com/fastapi/fastapi/pull/15805) by [@tiangolo](https://github.com/tiangolo).
* 📝 Update release notes link. PR [#15802](https://github.com/fastapi/fastapi/pull/15802) by [@tiangolo](https://github.com/tiangolo).
* ✏️ Update white space characters in bigger apps. PR [#15801](https://github.com/fastapi/fastapi/pull/15801) by [@tiangolo](https://github.com/tiangolo).
* ✏️ Fix grammar, typos, and broken links in docs. PR [#15694](https://github.com/fastapi/fastapi/pull/15694) by [@YuriiMotov](https://github.com/YuriiMotov).
### Translations
@@ -17,6 +28,9 @@ hide:
### Internal
* 🐛 Fix failing test, update format for raised errors. PR [#15804](https://github.com/fastapi/fastapi/pull/15804) by [@tiangolo](https://github.com/tiangolo).
* 👷 Fix test-alls-green. PR [#15803](https://github.com/fastapi/fastapi/pull/15803) by [@tiangolo](https://github.com/tiangolo).
* 🔧 Enable checking `release-notes.md` for typos. PR [#15796](https://github.com/fastapi/fastapi/pull/15796) by [@YuriiMotov](https://github.com/YuriiMotov).
* 📝 Tweak wording about deploying to FastAPI Cloud. PR [#15793](https://github.com/fastapi/fastapi/pull/15793) by [@tiangolo](https://github.com/tiangolo).
* 🔨 Use `gpt-5.5` model in `translate.py`, specify `-chat` to avoid warnings. PR [#15792](https://github.com/fastapi/fastapi/pull/15792) by [@YuriiMotov](https://github.com/YuriiMotov).

View File

@@ -17,16 +17,16 @@ Let's say you have a file structure like this:
```
.
├── app
   ├── __init__.py
   ├── main.py
   ├── dependencies.py
   └── routers
   │ ├── __init__.py
   │ ├── items.py
   │ └── users.py
   └── internal
   ├── __init__.py
   └── admin.py
├── __init__.py
├── main.py
├── dependencies.py
└── routers
│ ├── __init__.py
│ ├── items.py
│ └── users.py
└── internal
├── __init__.py
└── admin.py
```
/// tip

View File

@@ -0,0 +1,131 @@
# Frontend { #frontend }
You can serve static frontend apps with `app.frontend()` (or `router.frontend()`).
This is useful for frontend tools that generate static files, like React with Vite, TanStack Router, Astro, Vue, Svelte, Angular, Solid, and others.
With these tools, you normally have a step that builds the frontend, with a command like:
```bash
npm run build
```
That would generate a directory like `./dist/` with your frontend files.
You can use `app.frontend()` to serve that directory following the conventions needed by these frontend frameworks.
**FastAPI** checks *path operations* first. The frontend files are checked only if no normal route matched, so your API won't be affected.
## Serve a Frontend { #serve-a-frontend }
After building your frontend, for example with `npm run build`, put the generated files in a directory, for example, `dist`.
Your project structure could look like this:
```text
.
├── pyproject.toml
├── app
│ ├── __init__.py
│ └── main.py
└── dist
├── index.html
└── assets
└── app.js
```
Then serve it with `app.frontend()`:
{* ../../docs_src/frontend/tutorial001_py310.py hl[5] *}
With this, a request for `/assets/app.js` can serve `dist/assets/app.js`.
If you also have a **FastAPI** *path operation*, the *path operation* wins.
## Client-Side Routing { #client-side-routing }
Many frontend apps, including **single-page apps** (SPAs), use client-side routing. A path like `/dashboard/settings` might not be a real file but the framework would take care of handling it.
So, if accessing that URL directly (instead of navigating through the app), the backend should serve the frontend app from `index.html`, so that the frontend framework can then handle the client-side routing.
For that, use `fallback="index.html"`:
{* ../../docs_src/frontend/tutorial002_py310.py hl[5] *}
**FastAPI** uses this fallback only for requests that look like browser navigation. Missing files like JavaScript, CSS, and images still return `404`.
/// tip
By default, `fallback` has a value of `fallback="auto"`. In most cases you won't need to specify `fallback`. Read below for details.
///
This is what you would want with many frontend apps that use client-side routing, for example, React with TanStack Router, Vue, Angular, SvelteKit, or Solid.
## Custom 404 Page { #custom-404-page }
You can also serve a static `404.html` page for missing frontend paths:
{* ../../docs_src/frontend/tutorial003_py310.py hl[5] *}
That response keeps a status code of `404`.
In this case, **FastAPI** won't serve `index.html` for missing frontend paths. It will return the `404.html` file instead.
/// tip
By default, `fallback` has a value of `fallback="auto"`. With this, if a `404.html` file is found, it will be used as the fallback automatically.
So, you can normally omit the `fallback` argument.
///
This is useful with frontend tools that generate static HTML files for each page, like Astro.
## Fallback Auto { #fallback-auto }
By default, `app.frontend()` uses `fallback="auto"`.
If there is a `404.html` file in the frontend directory, missing frontend paths serve that file with status code `404`.
Otherwise, if there is an `index.html` file, missing browser navigation paths serve `index.html`, which is what many frontend apps with client-side routing expect.
So, in most cases you can use `app.frontend("/", directory="dist")` without specifying the `fallback` argument.
{* ../../docs_src/frontend/tutorial001_py310.py hl[5] *}
## Disable Fallback { #disable-fallback }
If you don't want to serve a fallback file for missing frontend paths, use `fallback=None`:
{* ../../docs_src/frontend/tutorial005_py310.py hl[5] *}
Then missing frontend paths return the normal `404`.
## Check Directory { #check-directory }
By default, `app.frontend()` checks that the directory exists when the app is created.
This helps catch configuration errors early. For example, if the frontend build output directory is missing, **FastAPI** will raise an error on startup.
If your frontend files are created later, for example by a separate build step after the app object is created, set `check_dir=False`:
{* ../../docs_src/frontend/tutorial006_py310.py hl[5] *}
With `check_dir=False`, **FastAPI** will not check the directory when the app is created. If the configured directory is still missing when a request is handled, **FastAPI** will raise an error then.
## Use it with `APIRouter` { #use-it-with-apirouter }
You can also add frontend files to an `APIRouter` and include it with a prefix:
{* ../../docs_src/frontend/tutorial004_py310.py hl[6,7] *}
In this example, frontend paths are served under `/app`.
Any regular *path operations* in the app will still take precedence, including in other routers.
## Static Build Output Only { #static-build-output-only }
`app.frontend()` serves files already generated by your frontend build.
It does not run server-side rendering. It is for frontend frameworks that generate static files, not for frameworks that need dynamic rendering on the server for each request.

View File

@@ -2,6 +2,14 @@
You can serve static files automatically from a directory using `StaticFiles`.
/// tip
If you need to host a frontend, use `app.frontend()` instead, read about it in [Frontend](frontend.md).
`app.frontend()` uses `StaticFiles` underneath, with several additional advantages for frontends, like handling client-side routing.
///
## Use `StaticFiles` { #use-staticfiles }
* Import `StaticFiles`.

View File

@@ -133,6 +133,7 @@ nav:
- tutorial/server-sent-events.md
- tutorial/background-tasks.md
- tutorial/metadata.md
- tutorial/frontend.md
- tutorial/static-files.md
- tutorial/testing.md
- tutorial/debugging.md

View File

@@ -1,485 +0,0 @@
# विकल्प, प्रेरणा और तुलनाएँ { #alternatives-inspiration-and-comparisons }
**FastAPI** को किससे प्रेरणा मिली, यह विकल्पों की तुलना में कैसा है और उनसे इसने क्या सीखा।
## परिचय { #intro }
दूसरों के पिछले काम के बिना **FastAPI** अस्तित्व में नहीं होता।
इससे पहले कई टूल बनाए गए हैं जिन्होंने इसके निर्माण को प्रेरित करने में मदद की।
मैं कई वर्षों तक एक नया framework बनाने से बचता रहा। पहले मैंने **FastAPI** द्वारा कवर की गई सभी विशेषताओं को कई अलग-अलग frameworks, plug-ins और tools का उपयोग करके हल करने की कोशिश की।
लेकिन एक समय ऐसा आया जब ऐसा कुछ बनाने के अलावा कोई विकल्प नहीं था जो ये सभी सुविधाएँ प्रदान करे, पिछले tools से सर्वोत्तम विचारों को लेकर, और उन्हें सबसे अच्छे तरीके से मिलाकर, उन language features का उपयोग करते हुए जो पहले उपलब्ध भी नहीं थे (Python 3.6+ type hints)।
## पिछले tools { #previous-tools }
### [Django](https://www.djangoproject.com/) { #django }
यह सबसे लोकप्रिय Python framework है और व्यापक रूप से भरोसेमंद है। इसका उपयोग Instagram जैसे systems बनाने के लिए किया जाता है।
यह relational databases (जैसे MySQL या PostgreSQL) के साथ अपेक्षाकृत tightly coupled है, इसलिए मुख्य store engine के रूप में NoSQL database (जैसे Couchbase, MongoDB, Cassandra, आदि) रखना बहुत आसान नहीं है।
इसे backend में HTML generate करने के लिए बनाया गया था, न कि किसी modern frontend (जैसे React, Vue.js और Angular) या इसके साथ संचार करने वाले अन्य systems (जैसे <abbr title="Internet of Things - इंटरनेट ऑफ थिंग्स">IoT</abbr> devices) द्वारा उपयोग की जाने वाली APIs बनाने के लिए।
### [Django REST Framework](https://www.django-rest-framework.org/) { #django-rest-framework }
Django REST Framework को Django के ऊपर Web APIs बनाने के लिए एक flexible toolkit के रूप में बनाया गया था, ताकि इसकी API क्षमताओं में सुधार हो सके।
इसका उपयोग Mozilla, Red Hat और Eventbrite सहित कई कंपनियाँ करती हैं।
यह **automatic API documentation** के पहले उदाहरणों में से एक था, और यह विशेष रूप से उन पहले विचारों में से एक था जिसने **FastAPI** की "खोज" को प्रेरित किया।
/// note | नोट
Django REST Framework को Tom Christie ने बनाया था। वही Starlette और Uvicorn के creator हैं, जिन पर **FastAPI** आधारित है।
///
/// tip | **FastAPI** को इससे प्रेरणा मिली कि
एक automatic API documentation web user interface हो।
///
### [Flask](https://flask.palletsprojects.com) { #flask }
Flask एक "microframework" है, इसमें database integrations या Django में default रूप से आने वाली कई चीज़ें शामिल नहीं हैं।
यह simplicity और flexibility मुख्य data storage system के रूप में NoSQL databases का उपयोग करने जैसी चीज़ें करने की अनुमति देती है।
क्योंकि यह बहुत सरल है, इसे सीखना अपेक्षाकृत सहज है, हालांकि documentation कुछ बिंदुओं पर थोड़ा technical हो जाता है।
इसका उपयोग आमतौर पर उन अन्य applications के लिए भी किया जाता है जिन्हें जरूरी नहीं कि database, user management, या Django में पहले से built-in आने वाली कई features की आवश्यकता हो। हालांकि इनमें से कई features plug-ins के साथ जोड़े जा सकते हैं।
Parts का यह decoupling, और एक "microframework" होना जिसे ठीक वही कवर करने के लिए extend किया जा सके जिसकी आवश्यकता है, एक key feature था जिसे मैं बनाए रखना चाहता था।
Flask की simplicity को देखते हुए, यह APIs बनाने के लिए अच्छा match लगा। अगली चीज़ जो खोजनी थी वह Flask के लिए एक "Django REST Framework" था।
/// tip | **FastAPI** को इससे प्रेरणा मिली कि
एक micro-framework हो। आवश्यक tools और parts को mix and match करना आसान बनाया जाए।
एक simple और उपयोग में आसान routing system हो।
///
### [Requests](https://requests.readthedocs.io) { #requests }
**FastAPI** वास्तव में **Requests** का विकल्प नहीं है। उनका scope बहुत अलग है।
वास्तव में FastAPI application के *अंदर* Requests का उपयोग करना सामान्य बात होगी।
लेकिन फिर भी, FastAPI को Requests से काफी प्रेरणा मिली।
**Requests** APIs के साथ *interact* करने के लिए (client के रूप में) एक library है, जबकि **FastAPI** APIs *बनाने* के लिए (server के रूप में) एक library है।
वे कमोबेश विपरीत सिरों पर हैं, एक-दूसरे को पूरक करते हुए।
Requests का design बहुत simple और intuitive है, sensible defaults के साथ इसका उपयोग करना बहुत आसान है। लेकिन साथ ही, यह बहुत powerful और customizable है।
इसीलिए, जैसा कि official website में कहा गया है:
> Requests अब तक के सबसे अधिक downloaded Python packages में से एक है
आप इसे जिस तरह उपयोग करते हैं वह बहुत सरल है। उदाहरण के लिए, `GET` request करने के लिए, आप लिखेंगे:
```Python
response = requests.get("http://example.com/some/url")
```
FastAPI में इसके समकक्ष API *path operation* इस तरह दिख सकता है:
```Python hl_lines="1"
@app.get("/some/url")
def read_url():
return {"message": "Hello World"}
```
`requests.get(...)` और `@app.get(...)` में समानताएँ देखें।
/// tip | **FastAPI** को इससे प्रेरणा मिली कि
* एक simple और intuitive API हो।
* HTTP method names (operations) को सीधे, straightforward और intuitive तरीके से उपयोग किया जाए।
* sensible defaults हों, लेकिन powerful customizations भी हों।
///
### [Swagger](https://swagger.io/) / [OpenAPI](https://github.com/OAI/OpenAPI-Specification/) { #swagger-openapi }
Django REST Framework से जो मुख्य feature मैं चाहता था वह automatic API documentation था।
फिर मुझे पता चला कि APIs को document करने के लिए JSON (या YAML, JSON का एक extension) का उपयोग करने वाला एक standard था, जिसे Swagger कहा जाता था।
और Swagger APIs के लिए एक web user interface पहले से बनाया जा चुका था। इसलिए, किसी API के लिए Swagger documentation generate कर पाना इस web user interface का automatically उपयोग करने की अनुमति देता।
एक समय पर, Swagger को Linux Foundation को दे दिया गया, ताकि उसका नाम बदलकर OpenAPI रखा जा सके।
इसीलिए version 2.0 के बारे में बात करते समय "Swagger" कहना आम है, और version 3+ के लिए "OpenAPI"।
/// tip | **FastAPI** को इससे प्रेरणा मिली कि
Custom schema के बजाय API specifications के लिए एक open standard अपनाया और उपयोग किया जाए।
और standards-based user interface tools को integrate किया जाए:
* [Swagger UI](https://github.com/swagger-api/swagger-ui)
* [ReDoc](https://github.com/Rebilly/ReDoc)
इन दोनों को इसलिए चुना गया क्योंकि ये काफी popular और stable थे, लेकिन एक quick search करने पर, आप OpenAPI के लिए दर्जनों alternative user interfaces पा सकते हैं (जिन्हें आप **FastAPI** के साथ उपयोग कर सकते हैं)।
///
### Flask REST frameworks { #flask-rest-frameworks }
कई Flask REST frameworks हैं, लेकिन उनकी जाँच में समय और काम लगाने के बाद, मैंने पाया कि कई discontinue या abandon हो चुके हैं, और उनमें कई unresolved issues हैं जिन्होंने उन्हें अनुपयुक्त बना दिया।
### [Marshmallow](https://marshmallow.readthedocs.io/en/stable/) { #marshmallow }
API systems द्वारा आवश्यक मुख्य features में से एक data "<dfn title="मार्शलिंग, रूपांतरण भी कहा जाता है">serialization</dfn>" है, जिसमें code (Python) से data लेकर उसे ऐसी चीज़ में बदला जाता है जिसे network के माध्यम से भेजा जा सके। उदाहरण के लिए, database से data रखने वाले object को JSON object में बदलना। `datetime` objects को strings में बदलना, आदि।
APIs द्वारा आवश्यक एक और बड़ा feature data validation है, यह सुनिश्चित करना कि data निश्चित parameters के अनुसार valid है। उदाहरण के लिए, कोई field `int` है, कोई random string नहीं। यह incoming data के लिए विशेष रूप से उपयोगी है।
Data validation system के बिना, आपको सभी checks हाथ से, code में करने पड़ते।
ये features वही हैं जिन्हें प्रदान करने के लिए Marshmallow बनाया गया था। यह एक बेहतरीन library है, और मैंने पहले इसका बहुत उपयोग किया है।
लेकिन इसे Python type hints के अस्तित्व में आने से पहले बनाया गया था। इसलिए, हर <dfn title="डेटा कैसे बना होना चाहिए इसकी परिभाषा">schema</dfn> को define करने के लिए आपको Marshmallow द्वारा प्रदान किए गए specific utils और classes का उपयोग करना पड़ता है।
/// tip | **FastAPI** को इससे प्रेरणा मिली कि
"schemas" को define करने के लिए code का उपयोग किया जाए जो data types और validation, automatically प्रदान करे।
///
### [Webargs](https://webargs.readthedocs.io/en/latest/) { #webargs }
APIs द्वारा आवश्यक एक और बड़ा feature incoming requests से data <dfn title="Python डेटा में पढ़ना और बदलना">parsing</dfn> करना है।
Webargs एक tool है जिसे Flask सहित कई frameworks के ऊपर यह प्रदान करने के लिए बनाया गया था।
यह data validation करने के लिए नीचे Marshmallow का उपयोग करता है। और इसे उन्हीं developers ने बनाया था।
यह एक बेहतरीन tool है और **FastAPI** से पहले मैंने इसका भी बहुत उपयोग किया था।
/// note | नोट
Webargs को उन्हीं Marshmallow developers ने बनाया था।
///
/// tip | **FastAPI** को इससे प्रेरणा मिली कि
Incoming request data का automatic validation हो।
///
### [APISpec](https://apispec.readthedocs.io/en/stable/) { #apispec }
Marshmallow और Webargs plug-ins के रूप में validation, parsing और serialization प्रदान करते हैं।
लेकिन documentation अभी भी missing है। फिर APISpec बनाया गया।
यह कई frameworks के लिए एक plug-in है (और Starlette के लिए भी एक plug-in है)।
यह जिस तरह काम करता है वह यह है कि आप route handle करने वाली प्रत्येक function की docstring के अंदर YAML format का उपयोग करके schema की definition लिखते हैं।
और यह OpenAPI schemas generate करता है।
Flask, Starlette, Responder, आदि में यह इसी तरह काम करता है।
लेकिन फिर, हमारे पास फिर से Python string (एक बड़ा YAML) के अंदर micro-syntax होने की समस्या है।
Editor इसमें ज्यादा मदद नहीं कर सकता। और अगर हम parameters या Marshmallow schemas modify करते हैं और उस YAML docstring को भी modify करना भूल जाते हैं, तो generated schema obsolete हो जाएगा।
/// note | नोट
APISpec को उन्हीं Marshmallow developers ने बनाया था।
///
/// tip | **FastAPI** को इससे प्रेरणा मिली कि
APIs के लिए open standard, OpenAPI को support किया जाए।
///
### [Flask-apispec](https://flask-apispec.readthedocs.io/en/latest/) { #flask-apispec }
यह एक Flask plug-in है, जो Webargs, Marshmallow और APISpec को एक साथ जोड़ता है।
यह APISpec का उपयोग करके OpenAPI schemas automatically generate करने के लिए Webargs और Marshmallow की जानकारी का उपयोग करता है।
यह एक बेहतरीन tool है, बहुत underrated। इसे वहाँ मौजूद कई Flask plug-ins से कहीं अधिक popular होना चाहिए। यह शायद इसकी documentation के बहुत concise और abstract होने के कारण हो सकता है।
इसने Python docstrings के अंदर YAML (एक और syntax) लिखने की आवश्यकता को हल कर दिया।
Flask, Flask-apispec को Marshmallow और Webargs के साथ मिलाकर यह combination **FastAPI** बनाने तक मेरा पसंदीदा backend stack था।
इसका उपयोग करने से कई Flask full-stack generators बने। ये वे main stacks हैं जिन्हें मैं (और कई external teams) अब तक उपयोग कर रहे हैं:
* [https://github.com/tiangolo/full-stack](https://github.com/tiangolo/full-stack)
* [https://github.com/tiangolo/full-stack-flask-couchbase](https://github.com/tiangolo/full-stack-flask-couchbase)
* [https://github.com/tiangolo/full-stack-flask-couchdb](https://github.com/tiangolo/full-stack-flask-couchdb)
और यही full-stack generators [**FastAPI** Project Generators](project-generation.md) का base थे।
/// note | नोट
Flask-apispec को उन्हीं Marshmallow developers ने बनाया था।
///
/// tip | **FastAPI** को इससे प्रेरणा मिली कि
OpenAPI schema को automatically generate किया जाए, उसी code से जो serialization और validation define करता है।
///
### [NestJS](https://nestjs.com/) (और [Angular](https://angular.io/)) { #nestjs-and-angular }
यह Python भी नहीं है, NestJS Angular से प्रेरित एक JavaScript (TypeScript) NodeJS framework है।
यह कुछ ऐसा हासिल करता है जो Flask-apispec के साथ किए जा सकने वाले काम जैसा है।
इसमें Angular 2 से प्रेरित एक integrated dependency injection system है। इसमें "injectables" को pre-register करना आवश्यक है (जैसे मुझे ज्ञात सभी अन्य dependency injection systems में), इसलिए, यह verbosity और code repetition बढ़ाता है।
क्योंकि parameters को TypeScript types (Python type hints के समान) के साथ describe किया जाता है, editor support काफी अच्छा है।
लेकिन क्योंकि TypeScript data compilation के बाद JavaScript में preserve नहीं रहता, यह validation, serialization और documentation को एक ही समय पर define करने के लिए types पर निर्भर नहीं हो सकता। इसके कारण और कुछ design decisions के कारण, validation, serialization और automatic schema generation पाने के लिए, कई जगह decorators जोड़ने की आवश्यकता होती है। इसलिए, यह काफी verbose हो जाता है।
यह nested models को बहुत अच्छी तरह handle नहीं कर सकता। इसलिए, अगर request में JSON body एक JSON object है जिसमें inner fields हैं जो खुद nested JSON objects हैं, तो इसे ठीक से document और validate नहीं किया जा सकता।
/// tip | **FastAPI** को इससे प्रेरणा मिली कि
बेहतरीन editor support के लिए Python types का उपयोग किया जाए।
एक powerful dependency injection system हो। Code repetition को minimize करने का तरीका खोजा जाए।
///
### [Sanic](https://sanic.readthedocs.io/en/latest/) { #sanic }
यह `asyncio` पर आधारित पहले बेहद तेज़ Python frameworks में से एक था। इसे Flask के बहुत समान बनाया गया था।
/// note | तकनीकी विवरण
इसने default Python `asyncio` loop के बजाय [`uvloop`](https://github.com/MagicStack/uvloop) का उपयोग किया। यही इसे इतना तेज़ बनाता था।
इसने स्पष्ट रूप से Uvicorn और Starlette को प्रेरित किया, जो वर्तमान में open benchmarks में Sanic से तेज़ हैं।
///
/// tip | **FastAPI** को इससे प्रेरणा मिली कि
बेहद तेज़ performance हासिल करने का तरीका खोजा जाए।
इसीलिए **FastAPI** Starlette पर आधारित है, क्योंकि यह उपलब्ध सबसे तेज़ framework है (third-party benchmarks द्वारा tested)।
///
### [Falcon](https://falconframework.org/) { #falcon }
Falcon एक और high performance Python framework है, इसे minimal होने और Hug जैसे अन्य frameworks की foundation के रूप में काम करने के लिए design किया गया है।
इसे ऐसी functions रखने के लिए design किया गया है जो दो parameters receive करती हैं, एक "request" और एक "response"। फिर आप request से parts "read" करते हैं, और response में parts "write" करते हैं। इस design के कारण, standard Python type hints के साथ function parameters के रूप में request parameters और bodies declare करना संभव नहीं है।
इसलिए, data validation, serialization, और documentation को code में करना पड़ता है, automatically नहीं। या उन्हें Falcon के ऊपर एक framework के रूप में implement करना पड़ता है, जैसे Hug। यही अंतर उन अन्य frameworks में भी होता है जो Falcon के design से प्रेरित हैं, जहाँ parameters के रूप में एक request object और एक response object होता है।
/// tip | **FastAPI** को इससे प्रेरणा मिली कि
बेहतरीन performance पाने के तरीके खोजे जाएँ।
Hug के साथ (क्योंकि Hug Falcon पर आधारित है) इसने **FastAPI** को functions में `response` parameter declare करने के लिए प्रेरित किया।
हालांकि FastAPI में यह optional है, और मुख्य रूप से headers, cookies, और alternative status codes set करने के लिए उपयोग किया जाता है।
///
### [Molten](https://moltenframework.com/) { #molten }
मैंने **FastAPI** बनाने के शुरुआती चरणों में Molten खोजा। और इसमें काफी समान विचार हैं:
* Python type hints पर आधारित।
* इन types से validation और documentation।
* Dependency Injection system।
यह Pydantic जैसी data validation, serialization और documentation third-party library का उपयोग नहीं करता, इसकी अपनी library है। इसलिए, ये data type definitions उतनी आसानी से reusable नहीं होंगी।
इसे थोड़ी अधिक verbose configurations की आवश्यकता होती है। और क्योंकि यह WSGI (ASGI के बजाय) पर आधारित है, इसे Uvicorn, Starlette और Sanic जैसे tools द्वारा प्रदान किए गए high performance का लाभ उठाने के लिए design नहीं किया गया है।
Dependency injection system को dependencies की pre-registration की आवश्यकता होती है और dependencies declared types के आधार पर solve की जाती हैं। इसलिए, किसी निश्चित type को provide करने वाले एक से अधिक "component" declare करना संभव नहीं है।
Routes एक ही जगह declare किए जाते हैं, दूसरी जगहों पर declared functions का उपयोग करके (decorators का उपयोग करने के बजाय जिन्हें endpoint handle करने वाली function के ठीक ऊपर रखा जा सकता है)। यह Flask (और Starlette) के तरीके की तुलना में Django के तरीके के अधिक करीब है। यह code में उन चीज़ों को अलग करता है जो अपेक्षाकृत tightly coupled हैं।
/// tip | **FastAPI** को इससे प्रेरणा मिली कि
Model attributes के "default" value का उपयोग करके data types के लिए extra validations define किए जाएँ। यह editor support को बेहतर बनाता है, और यह पहले Pydantic में उपलब्ध नहीं था।
इसने वास्तव में Pydantic के parts को update करने के लिए प्रेरित किया, ताकि वही validation declaration style support किया जा सके (यह सारी functionality अब Pydantic में पहले से उपलब्ध है)।
///
### [Hug](https://github.com/hugapi/hug) { #hug }
Hug उन पहले frameworks में से एक था जिसने Python type hints का उपयोग करके API parameter types की declaration implement की। यह एक बेहतरीन विचार था जिसने अन्य tools को भी ऐसा ही करने के लिए प्रेरित किया।
इसने अपनी declarations में standard Python types के बजाय custom types का उपयोग किया, लेकिन फिर भी यह एक बहुत बड़ा कदम आगे था।
यह पूरे API को JSON में declare करने वाला custom schema generate करने वाले पहले frameworks में से भी एक था।
यह OpenAPI और JSON Schema जैसे standard पर आधारित नहीं था। इसलिए इसे Swagger UI जैसे अन्य tools के साथ integrate करना straightforward नहीं होता। लेकिन फिर भी, यह एक बहुत innovative idea था।
इसमें एक दिलचस्प, uncommon feature है: उसी framework का उपयोग करके APIs और CLIs भी बनाना संभव है।
क्योंकि यह synchronous Python web frameworks (WSGI) के पिछले standard पर आधारित है, यह Websockets और अन्य चीज़ों को handle नहीं कर सकता, हालांकि इसका performance भी high है।
/// note | नोट
Hug को Timothy Crosley ने बनाया था, वही [`isort`](https://github.com/timothycrosley/isort) के creator हैं, जो Python files में imports को automatically sort करने के लिए एक बेहतरीन tool है।
///
/// tip | **FastAPI** को प्रेरित करने वाले विचार
Hug ने APIStar के parts को प्रेरित किया, और APIStar के साथ-साथ यह उन tools में से एक था जो मुझे सबसे promising लगे।
Hug ने **FastAPI** को parameters declare करने के लिए Python type hints का उपयोग करने, और API को automatically define करने वाला schema generate करने के लिए प्रेरित किया।
Hug ने **FastAPI** को headers और cookies set करने के लिए functions में `response` parameter declare करने के लिए प्रेरित किया।
///
### [APIStar](https://github.com/encode/apistar) (<= 0.5) { #apistar-0-5 }
**FastAPI** बनाने का निर्णय लेने से ठीक पहले मुझे **APIStar** server मिला। इसमें लगभग वह सब कुछ था जिसकी मुझे तलाश थी और इसका design बेहतरीन था।
यह उन पहले implementations में से एक था जो मैंने कभी देखे, जिसमें parameters और requests declare करने के लिए Python type hints का उपयोग करने वाला framework था (NestJS और Molten से पहले)। मुझे यह Hug के लगभग उसी समय मिला। लेकिन APIStar ने OpenAPI standard का उपयोग किया।
इसमें कई जगहों पर उन्हीं type hints के आधार पर automatic data validation, data serialization और OpenAPI schema generation था।
Body schema definitions Pydantic जैसे Python type hints का उपयोग नहीं करती थीं, यह Marshmallow के थोड़ा अधिक समान था, इसलिए editor support उतना अच्छा नहीं होता, लेकिन फिर भी, APIStar उपलब्ध सबसे अच्छा विकल्प था।
उस समय इसके performance benchmarks सबसे अच्छे थे (सिर्फ Starlette ने surpass किया था)।
शुरुआत में, इसमें automatic API documentation web UI नहीं था, लेकिन मुझे पता था कि मैं इसमें Swagger UI जोड़ सकता हूँ।
इसमें dependency injection system था। ऊपर चर्चा किए गए अन्य tools की तरह, इसमें components की pre-registration आवश्यक थी। लेकिन फिर भी, यह एक बेहतरीन feature था।
मैं कभी भी इसे full project में उपयोग नहीं कर पाया, क्योंकि इसमें security integration नहीं था, इसलिए मैं Flask-apispec पर आधारित full-stack generators के साथ मौजूद सभी features को replace नहीं कर सका। मेरे projects backlog में उस functionality को जोड़ने वाला pull request बनाने का विचार था।
लेकिन फिर, project का focus shift हो गया।
यह अब API web framework नहीं रहा, क्योंकि creator को Starlette पर focus करना था।
अब APIStar OpenAPI specifications validate करने के लिए tools का एक set है, web framework नहीं।
/// note | नोट
APIStar को Tom Christie ने बनाया था। वही व्यक्ति जिन्होंने बनाया:
* Django REST Framework
* Starlette (जिस पर **FastAPI** आधारित है)
* Uvicorn (Starlette और **FastAPI** द्वारा उपयोग किया जाता है)
///
/// tip | **FastAPI** को इससे प्रेरणा मिली कि
अस्तित्व में आए।
एक ही Python types के साथ कई चीज़ें (data validation, serialization और documentation) declare करने का विचार, जो साथ ही बेहतरीन editor support भी देता था, मुझे एक शानदार विचार लगा।
और लंबे समय तक समान framework की खोज करने और कई अलग-अलग alternatives को test करने के बाद, APIStar उपलब्ध सबसे अच्छा विकल्प था।
फिर APIStar ने server के रूप में अस्तित्व में रहना बंद कर दिया और Starlette बनाया गया, और यह ऐसे system के लिए एक नई बेहतर foundation था। यही **FastAPI** बनाने की अंतिम प्रेरणा थी।
मैं **FastAPI** को APIStar का "spiritual successor" मानता हूँ, जो इन सभी पिछले tools से मिली सीख के आधार पर features, typing system, और अन्य parts को improve और increase करता है।
///
## **FastAPI** द्वारा उपयोग किया गया { #used-by-fastapi }
### [Pydantic](https://docs.pydantic.dev/) { #pydantic }
Pydantic Python type hints के आधार पर data validation, serialization और documentation (JSON Schema का उपयोग करके) define करने के लिए एक library है।
यह इसे बेहद intuitive बनाता है।
यह Marshmallow से comparable है। हालांकि benchmarks में यह Marshmallow से तेज़ है। और क्योंकि यह उन्हीं Python type hints पर आधारित है, editor support बेहतरीन है।
/// tip | **FastAPI** इसे इन कामों के लिए उपयोग करता है
सभी data validation, data serialization और automatic model documentation (JSON Schema पर आधारित) handle करना।
फिर **FastAPI** उस JSON Schema data को लेता है और उसे OpenAPI में डालता है, उन सभी अन्य चीज़ों के अलावा जो यह करता है।
///
### [Starlette](https://www.starlette.dev/) { #starlette }
Starlette एक lightweight <dfn title="असिंक्रोनस Python web applications बनाने के लिए नया मानक">ASGI</dfn> framework/toolkit है, जो high-performance asyncio services बनाने के लिए ideal है।
यह बहुत simple और intuitive है। इसे आसानी से extensible होने और modular components रखने के लिए design किया गया है।
इसमें है:
* Seriously impressive performance.
* WebSocket support.
* In-process background tasks.
* Startup और shutdown events.
* HTTPX पर built test client.
* CORS, GZip, Static Files, Streaming responses.
* Session और Cookie support.
* 100% test coverage.
* 100% type annotated codebase.
* Few hard dependencies.
Starlette वर्तमान में tested सबसे तेज़ Python framework है। केवल Uvicorn ने इसे surpass किया है, जो framework नहीं, बल्कि server है।
Starlette सभी basic web microframework functionality प्रदान करता है।
लेकिन यह automatic data validation, serialization या documentation प्रदान नहीं करता।
यह उन मुख्य चीज़ों में से एक है जो **FastAPI** ऊपर से जोड़ता है, सब Python type hints (Pydantic का उपयोग करके) पर आधारित। इसके साथ dependency injection system, security utilities, OpenAPI schema generation, आदि।
/// note | तकनीकी विवरण
ASGI एक नया "standard" है जिसे Django core team members द्वारा develop किया जा रहा है। यह अभी भी "Python standard" (एक PEP) नहीं है, हालांकि वे ऐसा करने की प्रक्रिया में हैं।
फिर भी, इसे पहले से ही कई tools द्वारा "standard" के रूप में उपयोग किया जा रहा है। यह interoperability को बहुत बेहतर बनाता है, क्योंकि आप Uvicorn को किसी अन्य ASGI server (जैसे Daphne या Hypercorn) से switch कर सकते हैं, या आप ASGI compatible tools, जैसे `python-socketio`, जोड़ सकते हैं।
///
/// tip | **FastAPI** इसे इन कामों के लिए उपयोग करता है
सभी core web parts को handle करना। ऊपर से features जोड़ना।
Class `FastAPI` खुद सीधे class `Starlette` से inherit करती है।
इसलिए, जो कुछ भी आप Starlette के साथ कर सकते हैं, उसे आप सीधे **FastAPI** के साथ कर सकते हैं, क्योंकि यह मूल रूप से steroids पर Starlette है।
///
### [Uvicorn](https://www.uvicorn.dev/) { #uvicorn }
Uvicorn एक lightning-fast ASGI server है, जो uvloop और httptools पर बना है।
यह web framework नहीं, बल्कि server है। उदाहरण के लिए, यह paths द्वारा routing के लिए tools प्रदान नहीं करता। यह ऐसी चीज़ है जो Starlette (या **FastAPI**) जैसा framework ऊपर से प्रदान करेगा।
यह Starlette और **FastAPI** के लिए recommended server है।
/// tip | **FastAPI** इसे इस रूप में अनुशंसित करता है
**FastAPI** applications चलाने के लिए main web server।
आप asynchronous multi-process server पाने के लिए `--workers` command line option का भी उपयोग कर सकते हैं।
अधिक विवरण [Deployment](deployment/index.md) section में देखें।
///
## Benchmarks और speed { #benchmarks-and-speed }
Uvicorn, Starlette और FastAPI के बीच समझने, तुलना करने, और अंतर देखने के लिए, [Benchmarks](benchmarks.md) के बारे में section देखें।

View File

@@ -1,444 +0,0 @@
# Concurrency और async / await { #concurrency-and-async-await }
*path operation functions* के लिए `async def` syntax के बारे में विवरण और asynchronous code, concurrency, और parallelism के बारे में कुछ पृष्ठभूमि।
## जल्दी में हैं? { #in-a-hurry }
<abbr title="too long; didn't read - बहुत लंबा; नहीं पढ़ा"><strong>TL;DR:</strong></abbr>
अगर आप third party libraries का उपयोग कर रहे हैं जो आपको उन्हें `await` के साथ call करने को कहती हैं, जैसे:
```Python
results = await some_library()
```
तो, अपने *path operation functions* को `async def` के साथ declare करें, जैसे:
```Python hl_lines="2"
@app.get('/')
async def read_results():
results = await some_library()
return results
```
/// note | नोट
आप `await` का उपयोग केवल `async def` के साथ बनाए गए functions के अंदर ही कर सकते हैं।
///
---
अगर आप ऐसी third party library का उपयोग कर रहे हैं जो किसी चीज़ (database, API, file system, आदि) से communicate करती है और `await` का उपयोग करने का support नहीं रखती, (वर्तमान में अधिकांश database libraries के साथ यही स्थिति है), तो अपने *path operation functions* को सामान्य रूप से, केवल `def` के साथ declare करें, जैसे:
```Python hl_lines="2"
@app.get('/')
def results():
results = some_library()
return results
```
---
अगर आपके application को (किसी तरह) किसी और चीज़ से communicate करने और उसके response का इंतज़ार करने की ज़रूरत नहीं है, तो `async def` का उपयोग करें, भले ही आपको अंदर `await` का उपयोग करने की ज़रूरत न हो।
---
अगर आपको पता नहीं है, तो सामान्य `def` का उपयोग करें।
---
**नोट**: आप अपनी ज़रूरत के अनुसार अपने *path operation functions* में `def` और `async def` को mix कर सकते हैं और हर एक को अपने लिए सबसे अच्छे option का उपयोग करके define कर सकते हैं। FastAPI उनके साथ सही काम करेगा।
किसी भी स्थिति में, ऊपर दिए गए किसी भी case में, FastAPI फिर भी asynchronously काम करेगा और बेहद तेज़ होगा।
लेकिन ऊपर दिए गए steps को follow करने से, यह कुछ performance optimizations कर पाएगा।
## तकनीकी विवरण { #technical-details }
Python के आधुनिक versions **"asynchronous code"** का support करते हैं, जो **"coroutines"** नाम की चीज़ का उपयोग करता है, **`async` और `await`** syntax के साथ।
आइए नीचे के sections में इस phrase को हिस्सों में देखते हैं:
* **Asynchronous Code**
* **`async` और `await`**
* **Coroutines**
## Asynchronous Code { #asynchronous-code }
Asynchronous code का मतलब बस यह है कि language 💬 के पास computer / program 🤖 को यह बताने का एक तरीका होता है कि code में किसी point पर, उसे 🤖 कहीं और *किसी और चीज़* के finish होने का इंतज़ार करना होगा। मान लें कि उस *किसी और चीज़* को "slow-file" 📝 कहा जाता है।
तो, उस समय के दौरान, computer कोई और काम कर सकता है, जबकि "slow-file" 📝 finish हो रही होती है।
फिर computer / program 🤖 हर बार वापस आएगा जब उसे मौका मिलेगा क्योंकि वह फिर से इंतज़ार कर रहा होगा, या जब भी वह 🤖 उस point पर अपना सारा काम finish कर लेगा। और वह 🤖 देखेगा कि जिन tasks का वह इंतज़ार कर रहा था, उनमें से कोई पहले ही finish हो चुका है या नहीं, फिर वह जो भी करना था करेगा।
इसके बाद, वह 🤖 finish होने वाला पहला task लेता है (मान लें, हमारी "slow-file" 📝) और उसके साथ जो भी करना था उसे जारी रखता है।
वह "किसी और चीज़ का इंतज़ार" आम तौर पर <abbr title="Input and Output - इनपुट और आउटपुट">I/O</abbr> operations को refer करता है जो अपेक्षाकृत "slow" होते हैं (processor और RAM memory की speed की तुलना में), जैसे इंतज़ार करना:
* client से data network के माध्यम से भेजे जाने का
* आपके program द्वारा भेजा गया data client द्वारा network के माध्यम से receive किए जाने का
* disk पर किसी file की contents system द्वारा पढ़े जाने और आपके program को दिए जाने का
* आपके program द्वारा system को दी गई contents disk पर लिखे जाने का
* किसी remote API operation का
* किसी database operation के finish होने का
* किसी database query द्वारा results return किए जाने का
* आदि।
क्योंकि execution time ज़्यादातर <abbr title="Input and Output - इनपुट और आउटपुट">I/O</abbr> operations का इंतज़ार करने में consume होता है, उन्हें "I/O bound" operations कहा जाता है।
इसे "asynchronous" इसलिए कहा जाता है क्योंकि computer / program को slow task के साथ "synchronized" होने की ज़रूरत नहीं होती, task के finish होने के exact moment का इंतज़ार करते हुए, कुछ भी न करते हुए, ताकि वह task result ले सके और काम जारी रख सके।
इसके बजाय, "asynchronous" system होने के कारण, task finish होने के बाद, computer / program के वापस आने तक थोड़ा सा line में wait कर सकता है (कुछ microseconds), ताकि computer / program जो काम करने गया था उसे finish करे, और फिर वापस आकर results ले और उनके साथ काम जारी रखे।
"Synchronous" ("asynchronous" के विपरीत) के लिए वे आमतौर पर "sequential" term भी use करते हैं, क्योंकि computer / program किसी अलग task पर switch करने से पहले sequence में सभी steps follow करता है, भले ही उन steps में इंतज़ार शामिल हो।
### Concurrency और Burgers { #concurrency-and-burgers }
ऊपर describe किए गए **asynchronous** code के इस idea को कभी-कभी **"concurrency"** भी कहा जाता है। यह **"parallelism"** से अलग है।
**Concurrency** और **parallelism** दोनों "अलग-अलग चीज़ें लगभग एक ही समय पर हो रही हैं" से related हैं।
लेकिन *concurrency* और *parallelism* के बीच के details काफी अलग हैं।
अंतर देखने के लिए, burgers के बारे में निम्न story imagine करें:
### Concurrent Burgers { #concurrent-burgers }
आप अपनी crush के साथ fast food लेने जाते हैं, आप line में खड़े होते हैं जबकि cashier आपके आगे के लोगों से orders ले रहा होता है। 😍
<img src="/img/async/concurrent-burgers/concurrent-burgers-01.png" class="illustration">
फिर आपकी बारी आती है, आप अपनी crush और अपने लिए 2 बहुत fancy burgers का order देते हैं। 🍔🍔
<img src="/img/async/concurrent-burgers/concurrent-burgers-02.png" class="illustration">
Cashier kitchen में cook से कुछ कहता है ताकि उन्हें पता हो कि उन्हें आपके burgers prepare करने हैं (भले ही वे currently previous clients के burgers prepare कर रहे हों)।
<img src="/img/async/concurrent-burgers/concurrent-burgers-03.png" class="illustration">
आप pay करते हैं। 💸
Cashier आपको आपकी turn का number देता है।
<img src="/img/async/concurrent-burgers/concurrent-burgers-04.png" class="illustration">
जब आप wait कर रहे होते हैं, आप अपनी crush के साथ एक table चुनते हैं, बैठते हैं और अपनी crush से लंबे समय तक बात करते हैं (क्योंकि आपके burgers बहुत fancy हैं और prepare होने में कुछ समय लेते हैं)।
जब आप अपनी crush के साथ table पर बैठे होते हैं, burgers का इंतज़ार करते हुए, आप उस समय को यह admire करने में spend कर सकते हैं कि आपकी crush कितनी awesome, cute और smart है ✨😍✨।
<img src="/img/async/concurrent-burgers/concurrent-burgers-05.png" class="illustration">
Wait करते हुए और अपनी crush से बात करते हुए, time to time, आप counter पर displayed number check करते हैं कि क्या आपकी turn आ गई है।
फिर किसी point पर, आखिरकार आपकी turn आ जाती है। आप counter पर जाते हैं, अपने burgers लेते हैं और table पर वापस आते हैं।
<img src="/img/async/concurrent-burgers/concurrent-burgers-06.png" class="illustration">
आप और आपकी crush burgers खाते हैं और अच्छा समय बिताते हैं। ✨
<img src="/img/async/concurrent-burgers/concurrent-burgers-07.png" class="illustration">
/// note | नोट
सुंदर illustrations [Ketrina Thompson](https://www.instagram.com/ketrinadrawsalot) द्वारा। 🎨
///
---
कल्पना करें कि उस story में आप computer / program 🤖 हैं।
जब आप line में होते हैं, आप बस idle 😴 होते हैं, अपनी turn का इंतज़ार करते हुए, कुछ बहुत "productive" नहीं कर रहे होते। लेकिन line fast है क्योंकि cashier केवल orders ले रहा है (उन्हें prepare नहीं कर रहा), इसलिए यह ठीक है।
फिर, जब आपकी turn आती है, आप actual "productive" work करते हैं, menu process करते हैं, decide करते हैं कि आपको क्या चाहिए, अपनी crush की choice लेते हैं, pay करते हैं, check करते हैं कि आप correct bill या card दे रहे हैं, check करते हैं कि आपसे सही charge किया गया है, check करते हैं कि order में correct items हैं, आदि।
लेकिन फिर, भले ही आपके पास अभी burgers नहीं हैं, cashier के साथ आपका work "on pause" ⏸ है, क्योंकि आपको अपने burgers ready होने का wait 🕙 करना है।
लेकिन जब आप counter से दूर जाते हैं और अपनी turn के number के साथ table पर बैठते हैं, आप अपना attention अपनी crush पर switch 🔀 कर सकते हैं, और उस पर "work" ⏯ 🤓 कर सकते हैं। फिर आप दोबारा कुछ बहुत "productive" कर रहे होते हैं, जैसे अपनी crush 😍 के साथ flirting।
फिर cashier 💁 counter के display पर आपका number डालकर कहता है "मैंने burgers बना लिए हैं", लेकिन displayed number आपकी turn number में बदलते ही आप तुरंत पागलों की तरह jump नहीं करते। आपको पता है कि कोई आपके burgers नहीं चुराएगा क्योंकि आपके पास आपकी turn का number है, और उनके पास उनका।
तो आप अपनी crush के story finish करने का इंतज़ार करते हैं (current work ⏯ / process हो रहा task 🤓 finish होना), gentle smile करते हैं और कहते हैं कि आप burgers लेने जा रहे हैं ⏸।
फिर आप counter 🔀 पर जाते हैं, उस initial task पर जो अब finish हो चुका है ⏯, burgers उठाते हैं, thanks कहते हैं और उन्हें table पर ले जाते हैं। इससे counter के साथ interaction का वह step / task finish हो जाता है ⏹। यह बदले में, "eating burgers" 🔀 ⏯ का एक नया task create करता है, लेकिन "getting burgers" वाला previous task finish हो चुका है ⏹।
### Parallel Burgers { #parallel-burgers }
अब imagine करें कि ये "Concurrent Burgers" नहीं, बल्कि "Parallel Burgers" हैं।
आप अपनी crush के साथ parallel fast food लेने जाते हैं।
आप line में खड़े होते हैं जबकि कई (मान लें 8) cashiers, जो उसी समय cooks भी हैं, आपके आगे के लोगों से orders ले रहे होते हैं।
आपसे पहले हर कोई counter छोड़ने से पहले अपने burgers ready होने का wait कर रहा है क्योंकि 8 cashiers में से हर एक next order लेने से पहले तुरंत जाकर burger prepare करता है।
<img src="/img/async/parallel-burgers/parallel-burgers-01.png" class="illustration">
फिर आखिरकार आपकी turn आती है, आप अपनी crush और अपने लिए 2 बहुत fancy burgers का order देते हैं।
आप pay करते हैं 💸।
<img src="/img/async/parallel-burgers/parallel-burgers-02.png" class="illustration">
Cashier kitchen में जाता है।
आप counter 🕙 के सामने खड़े होकर wait करते हैं, ताकि आपसे पहले कोई और आपके burgers न ले जाए, क्योंकि turns के लिए कोई numbers नहीं हैं।
<img src="/img/async/parallel-burgers/parallel-burgers-03.png" class="illustration">
क्योंकि आप और आपकी crush इस बात में busy हैं कि कोई आपके आगे न आ जाए और आपके burgers आते ही उन्हें न ले जाए, आप अपनी crush पर attention नहीं दे सकते। 😞
यह "synchronous" work है, आप cashier/cook 👨‍🍳 के साथ "synchronized" हैं। आपको wait 🕙 करना है और exact moment पर वहाँ होना है जब cashier/cook 👨‍🍳 burgers finish करता है और आपको देता है, नहीं तो कोई और उन्हें ले सकता है।
<img src="/img/async/parallel-burgers/parallel-burgers-04.png" class="illustration">
फिर आपका cashier/cook 👨‍🍳 लंबे समय तक counter के सामने wait 🕙 कराने के बाद आखिरकार आपके burgers लेकर वापस आता है।
<img src="/img/async/parallel-burgers/parallel-burgers-05.png" class="illustration">
आप अपने burgers लेते हैं और अपनी crush के साथ table पर जाते हैं।
आप बस उन्हें खाते हैं, और आपका काम हो जाता है। ⏹
<img src="/img/async/parallel-burgers/parallel-burgers-06.png" class="illustration">
ज़्यादा बात या flirting नहीं हुई क्योंकि अधिकतर समय counter के सामने wait 🕙 करने में spend हुआ। 😞
/// note | नोट
सुंदर illustrations [Ketrina Thompson](https://www.instagram.com/ketrinadrawsalot) द्वारा। 🎨
///
---
Parallel burgers के इस scenario में, आप एक computer / program 🤖 हैं जिसमें दो processors हैं (आप और आपकी crush), दोनों wait 🕙 कर रहे हैं और लंबे समय तक "counter पर waiting" 🕙 में अपना attention ⏯ dedicate कर रहे हैं।
Fast food store में 8 processors (cashiers/cooks) हैं। जबकि concurrent burgers store में शायद केवल 2 (एक cashier और एक cook) रहे होंगे।
लेकिन फिर भी, final experience सबसे अच्छा नहीं है। 😞
---
यह burgers के लिए parallel equivalent story होगी। 🍔
इसके एक और "real life" example के लिए, एक bank imagine करें।
हाल तक, अधिकांश banks में multiple cashiers 👨‍💼👨‍💼👨‍💼👨‍💼 और एक बड़ी line 🕙🕙🕙🕙🕙🕙🕙🕙 होती थी।
सभी cashiers एक client के बाद दूसरे client के साथ सारा काम कर रहे होते थे 👨‍💼⏯।
और आपको line में लंबे समय तक wait 🕙 करना पड़ता है या आप अपनी turn खो देते हैं।
आप शायद अपनी crush 😍 को bank 🏦 में errands करने के लिए अपने साथ नहीं ले जाना चाहेंगे।
### Burger निष्कर्ष { #burger-conclusion }
"अपनी crush के साथ fast food burgers" के इस scenario में, क्योंकि बहुत waiting 🕙 है, concurrent system ⏸🔀⏯ रखना कहीं अधिक sensible है।
अधिकांश web applications के लिए यही case है।
बहुत, बहुत सारे users, लेकिन आपका server उनकी not-so-good connection द्वारा उनकी requests भेजने का wait 🕙 कर रहा है।
और फिर responses वापस आने का फिर से wait 🕙 कर रहा है।
यह "waiting" 🕙 microseconds में measure की जाती है, लेकिन फिर भी, सबको जोड़ने पर, अंत में काफी waiting हो जाती है।
इसीलिए web APIs के लिए asynchronous ⏸🔀⏯ code use करना बहुत sensible है।
इसी तरह की asynchronicity ने NodeJS को popular बनाया (भले ही NodeJS parallel नहीं है) और यही Go की programming language के रूप में strength है।
और यही same level की performance आपको **FastAPI** के साथ मिलती है।
और क्योंकि आपके पास parallelism और asynchronicity एक ही समय पर हो सकते हैं, आपको tested NodeJS frameworks में से अधिकांश से higher performance मिलती है और Go के बराबर, जो C के करीब एक compiled language है [(यह सब Starlette के कारण)](https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=query&l=zijmkf-1)।
### क्या concurrency parallelism से बेहतर है? { #is-concurrency-better-than-parallelism }
नहीं! यह story की moral नहीं है।
Concurrency, parallelism से अलग है। और यह उन **specific** scenarios में बेहतर है जिनमें बहुत waiting शामिल होती है। इसी कारण, web application development के लिए यह आम तौर पर parallelism से बहुत बेहतर है। लेकिन हर चीज़ के लिए नहीं।
तो, इसे balance करने के लिए, निम्न short story imagine करें:
> आपको एक बड़ा, गंदा house clean करना है।
*हाँ, यही पूरी story है*।
---
कहीं भी कोई waiting 🕙 नहीं है, बस बहुत सारा काम करना है, house के multiple places में।
आप burgers example की तरह turns रख सकते थे, पहले living room, फिर kitchen, लेकिन क्योंकि आप किसी चीज़ का wait 🕙 नहीं कर रहे, बस cleaning and cleaning कर रहे हैं, turns किसी चीज़ को affect नहीं करेंगे।
Turns (concurrency) के साथ या बिना finish करने में उतना ही time लगेगा और आपने उतना ही काम किया होगा।
लेकिन इस case में, अगर आप 8 ex-cashier/cooks/now-cleaners ला सकें, और उनमें से हर एक (plus आप) house का एक zone clean करने के लिए ले सके, तो आप extra help के साथ सारा काम **parallel** में कर सकते हैं, और बहुत जल्दी finish कर सकते हैं।
इस scenario में, cleaners में से हर एक (आप सहित) एक processor होगा, job का अपना part कर रहा होगा।
और क्योंकि execution time का अधिकतर हिस्सा actual work (waiting के बजाय) में लगता है, और computer में work <abbr title="Central Processing Unit - केंद्रीय प्रसंस्करण इकाई">CPU</abbr> द्वारा किया जाता है, वे इन problems को "CPU bound" कहते हैं।
---
CPU bound operations के common examples ऐसी चीज़ें हैं जिन्हें complex math processing की ज़रूरत होती है।
उदाहरण के लिए:
* **Audio** या **image processing**।
* **Computer vision**: एक image millions of pixels से composed होती है, हर pixel में 3 values / colors होते हैं, उसे process करने के लिए आम तौर पर उन pixels पर कुछ compute करना पड़ता है, सब एक ही समय पर।
* **Machine Learning**: इसमें आम तौर पर बहुत सारे "matrix" और "vector" multiplications की ज़रूरत होती है। Numbers वाली एक huge spreadsheet के बारे में सोचें और उन सभी को एक ही समय पर multiply करना।
* **Deep Learning**: यह Machine Learning का sub-field है, इसलिए, वही लागू होता है। बस इतना है कि multiply करने के लिए numbers की एक single spreadsheet नहीं होती, बल्कि उनका huge set होता है, और कई cases में, आप उन models को build और / या use करने के लिए एक special processor का उपयोग करते हैं।
### Concurrency + Parallelism: Web + Machine Learning { #concurrency-parallelism-web-machine-learning }
**FastAPI** के साथ आप concurrency का advantage ले सकते हैं जो web development के लिए बहुत common है (NodeJS का वही main attraction)।
लेकिन आप Machine Learning systems जैसे **CPU bound** workloads के लिए parallelism और multiprocessing (multiple processes parallel में चलाना) के benefits का भी exploit कर सकते हैं।
वह, साथ ही यह simple fact कि Python **Data Science**, Machine Learning और खासकर Deep Learning की main language है, FastAPI को Data Science / Machine Learning web APIs और applications (कई अन्य के बीच) के लिए बहुत अच्छा match बनाता है।
Production में इस parallelism को कैसे achieve करें, यह देखने के लिए [Deployment](deployment/index.md) के बारे में section देखें।
## `async` और `await` { #async-and-await }
Python के आधुनिक versions में asynchronous code define करने का बहुत intuitive तरीका है। इससे यह सामान्य "sequential" code जैसा दिखता है और सही moments पर आपके लिए "awaiting" करता है।
जब कोई operation results देने से पहले waiting require करेगा और इन नए Python features का support रखता है, तो आप उसे ऐसे code कर सकते हैं:
```Python
burgers = await get_burgers(2)
```
यहाँ key `await` है। यह Python को बताता है कि `burgers` में results store करने से पहले उसे `get_burgers(2)` के अपना काम 🕙 finish करने का wait ⏸ करना है। इससे, Python जान जाएगा कि वह meanwhile कुछ और 🔀 ⏯ कर सकता है (जैसे कोई दूसरी request receive करना)।
`await` के work करने के लिए, इसे ऐसे function के अंदर होना चाहिए जो इस asynchronicity को support करता हो। ऐसा करने के लिए, आप बस इसे `async def` के साथ declare करते हैं:
```Python hl_lines="1"
async def get_burgers(number: int):
# Burgers बनाने के लिए कुछ asynchronous काम करें
return burgers
```
...`def` के बजाय:
```Python hl_lines="2"
# यह asynchronous नहीं है
def get_sequential_burgers(number: int):
# Burgers बनाने के लिए कुछ sequential काम करें
return burgers
```
`async def` के साथ, Python जानता है कि उस function के अंदर उसे `await` expressions के बारे में aware रहना है, और वह उस function के execution को "pause" ⏸ कर सकता है और वापस आने से पहले कुछ और 🔀 कर सकता है।
जब आप किसी `async def` function को call करना चाहते हैं, तो आपको उसे "await" करना होता है। इसलिए, यह काम नहीं करेगा:
```Python
# यह काम नहीं करेगा, क्योंकि get_burgers को async def के साथ define किया गया था
burgers = get_burgers(2)
```
---
तो, अगर आप कोई library use कर रहे हैं जो आपको बताती है कि आप उसे `await` के साथ call कर सकते हैं, तो आपको उसका उपयोग करने वाले *path operation functions* को `async def` के साथ create करना होगा, जैसे:
```Python hl_lines="2-3"
@app.get('/burgers')
async def read_burgers():
burgers = await get_burgers(2)
return burgers
```
### अधिक तकनीकी विवरण { #more-technical-details }
आपने notice किया होगा कि `await` केवल `async def` के साथ defined functions के अंदर ही use किया जा सकता है।
लेकिन उसी समय, `async def` के साथ defined functions को "awaited" होना पड़ता है। इसलिए, `async def` वाले functions केवल `async def` के साथ defined functions के अंदर ही call किए जा सकते हैं।
तो, egg और chicken के बारे में, आप first `async` function को कैसे call करते हैं?
अगर आप **FastAPI** के साथ काम कर रहे हैं तो आपको इसकी चिंता करने की ज़रूरत नहीं है, क्योंकि वह "first" function आपका *path operation function* होगा, और FastAPI जानता होगा कि सही काम कैसे करना है।
लेकिन अगर आप FastAPI के बिना `async` / `await` use करना चाहते हैं, तो आप ऐसा भी कर सकते हैं।
### अपना async code लिखें { #write-your-own-async-code }
Starlette (और **FastAPI**) [AnyIO](https://anyio.readthedocs.io/en/stable/) पर based हैं, जो इसे Python की standard library [asyncio](https://docs.python.org/3/library/asyncio-task.html) और [Trio](https://trio.readthedocs.io/en/stable/) दोनों के साथ compatible बनाता है।
विशेष रूप से, आप अपने advanced concurrency use cases के लिए सीधे [AnyIO](https://anyio.readthedocs.io/en/stable/) use कर सकते हैं जिन्हें आपके अपने code में अधिक advanced patterns की ज़रूरत होती है।
और भले ही आप FastAPI use नहीं कर रहे हों, आप high compatibility और इसके benefits (जैसे *structured concurrency*) पाने के लिए [AnyIO](https://anyio.readthedocs.io/en/stable/) के साथ अपने खुद के async applications भी लिख सकते हैं।
मैंने AnyIO के ऊपर एक और library बनाई, ऊपर एक thin layer के रूप में, ताकि type annotations को थोड़ा improve किया जा सके और बेहतर **autocompletion**, **inline errors**, आदि मिल सकें। इसमें एक friendly introduction और tutorial भी है ताकि आपको **समझने** और **अपना async code** लिखने में मदद मिले: [Asyncer](https://asyncer.tiangolo.com/)। यह विशेष रूप से useful होगा अगर आपको **async code को regular** (blocking/synchronous) code के साथ **combine** करना हो।
### Asynchronous code के अन्य forms { #other-forms-of-asynchronous-code }
`async` और `await` use करने की यह style language में relatively new है।
लेकिन यह asynchronous code के साथ काम करना बहुत आसान बनाती है।
यही same syntax (या लगभग identical) हाल ही में JavaScript के modern versions (Browser और NodeJS में) में भी शामिल किया गया था।
लेकिन उससे पहले, asynchronous code handle करना कहीं अधिक complex और difficult था।
Python के previous versions में, आप threads या [Gevent](https://www.gevent.org/) use कर सकते थे। लेकिन code समझने, debug करने और उसके बारे में सोचने के लिए कहीं अधिक complex होता है।
NodeJS / Browser JavaScript के previous versions में, आप "callbacks" use करते। जो "callback hell" तक ले जाता है।
## Coroutines { #coroutines }
**Coroutine** बस उस चीज़ के लिए बहुत fancy term है जो किसी `async def` function द्वारा return की जाती है। Python जानता है कि यह function जैसी कोई चीज़ है, जिसे वह start कर सकता है और जो किसी point पर end होगी, लेकिन यह internally pause ⏸ भी हो सकती है, जब भी इसके अंदर कोई `await` हो।
लेकिन asynchronous code को `async` और `await` के साथ use करने की यह सारी functionality कई बार "coroutines" use करने के रूप में summarize की जाती है। यह Go की main key feature, "Goroutines" से comparable है।
## निष्कर्ष { #conclusion }
आइए ऊपर वाला वही phrase देखें:
> Python के आधुनिक versions **"asynchronous code"** का support करते हैं, जो **"coroutines"** नाम की चीज़ का उपयोग करता है, **`async` और `await`** syntax के साथ।
अब यह अधिक sense बनाना चाहिए। ✨
यह सब FastAPI (Starlette के माध्यम से) को power करता है और इसे इतनी impressive performance देता है।
## बहुत तकनीकी विवरण { #very-technical-details }
/// warning | चेतावनी
आप शायद इसे skip कर सकते हैं।
ये **FastAPI** अंदर से कैसे काम करता है, इसके बहुत technical details हैं।
अगर आपके पास काफी technical knowledge (coroutines, threads, blocking, आदि) है और आप curious हैं कि FastAPI `async def` बनाम normal `def` को कैसे handle करता है, तो आगे बढ़ें।
///
### Path operation functions { #path-operation-functions }
जब आप किसी *path operation function* को `async def` के बजाय normal `def` के साथ declare करते हैं, तो इसे directly call करने के बजाय (क्योंकि यह server को block करेगा), एक external threadpool में run किया जाता है जिसे फिर awaited किया जाता है।
अगर आप किसी दूसरे async framework से आ रहे हैं जो ऊपर described तरीके से काम नहीं करता और आप tiny performance gain (लगभग 100 nanoseconds) के लिए trivial compute-only *path operation functions* को plain `def` के साथ define करने के आदी हैं, तो कृपया ध्यान दें कि **FastAPI** में effect बिल्कुल उल्टा होगा। इन cases में, `async def` use करना बेहतर है, जब तक कि आपके *path operation functions* ऐसा code use न करें जो blocking <abbr title="Input/Output - इनपुट/आउटपुट: डिस्क पढ़ना या लिखना, network communications.">I/O</abbr> perform करता हो।
फिर भी, दोनों situations में, संभावना है कि **FastAPI** आपके previous framework से [फिर भी तेज़ होगा](index.md#performance) (या कम से कम comparable होगा)।
### Dependencies { #dependencies }
[Dependencies](tutorial/dependencies/index.md) के लिए भी यही लागू होता है। अगर कोई dependency `async def` के बजाय standard `def` function है, तो उसे external threadpool में run किया जाता है।
### Sub-dependencies { #sub-dependencies }
आपके पास multiple dependencies और [sub-dependencies](tutorial/dependencies/sub-dependencies.md) हो सकती हैं जो एक-दूसरे को require करती हैं (function definitions के parameters के रूप में), उनमें से कुछ `async def` के साथ created हो सकती हैं और कुछ normal `def` के साथ। यह फिर भी work करेगा, और normal `def` के साथ created ones को "awaited" होने के बजाय external thread (threadpool से) पर call किया जाएगा।
### अन्य utility functions { #other-utility-functions }
कोई भी अन्य utility function जिसे आप directly call करते हैं, normal `def` या `async def` के साथ created हो सकता है और FastAPI आपके उसे call करने के तरीके को affect नहीं करेगा।
यह उन functions के contrast में है जिन्हें FastAPI आपके लिए call करता है: *path operation functions* और dependencies।
अगर आपका utility function `def` वाला normal function है, तो उसे directly call किया जाएगा (जैसा आप अपने code में लिखते हैं), threadpool में नहीं; अगर function `async def` के साथ created है तो आपको अपने code में उसे call करते समय उस function को `await` करना चाहिए।
---
फिर से, ये बहुत technical details हैं जो शायद useful होंगे अगर आप इन्हें search करते हुए आए हैं।
अन्यथा, आपको ऊपर के section की guidelines के साथ ठीक होना चाहिए: <a href="#in-a-hurry">जल्दी में हैं?</a>।

View File

@@ -1,34 +0,0 @@
# Benchmarks { #benchmarks }
स्वतंत्र TechEmpower benchmarks दिखाते हैं कि Uvicorn के अंतर्गत चलने वाले **FastAPI** applications [उपलब्ध सबसे तेज़ Python frameworks में से एक](https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7) हैं, केवल Starlette और Uvicorn स्वयं से नीचे (जिनका उपयोग FastAPI द्वारा internally किया जाता है)।
लेकिन benchmarks और comparisons देखते समय आपको निम्न बातों को ध्यान में रखना चाहिए।
## Benchmarks और speed { #benchmarks-and-speed }
जब आप benchmarks देखते हैं, तो अलग-अलग प्रकार के कई tools को equivalent मानकर compare होते देखना आम बात है।
विशेष रूप से, Uvicorn, Starlette और FastAPI को एक साथ compare होते देखना (कई अन्य tools के साथ)।
Tool जितनी सरल समस्या हल करता है, उसे उतनी ही बेहतर performance मिलेगी। और अधिकांश benchmarks, tool द्वारा प्रदान की जाने वाली अतिरिक्त features को test नहीं करते।
Hierarchy इस प्रकार है:
* **Uvicorn**: एक ASGI server
* **Starlette**: (Uvicorn का उपयोग करता है) एक web microframework
* **FastAPI**: (Starlette का उपयोग करता है) APIs बनाने के लिए कई अतिरिक्त features वाला एक API microframework, जिसमें data validation आदि शामिल हैं।
* **Uvicorn**:
* इसकी performance सबसे अच्छी होगी, क्योंकि इसमें server स्वयं के अलावा बहुत अधिक extra code नहीं होता।
* आप सीधे Uvicorn में application नहीं लिखेंगे। इसका मतलब होगा कि आपके code में कमोबेश, कम से कम, Starlette (या **FastAPI**) द्वारा प्रदान किया गया सारा code शामिल करना पड़ेगा। और यदि आपने ऐसा किया, तो आपके final application में framework का उपयोग करने और अपने app code तथा bugs को कम करने जितना ही overhead होगा।
* यदि आप Uvicorn की तुलना कर रहे हैं, तो इसकी तुलना Daphne, Hypercorn, uWSGI आदि से करें। Application servers से।
* **Starlette**:
* Uvicorn के बाद इसकी performance अगली सबसे अच्छी होगी। वास्तव में, Starlette चलने के लिए Uvicorn का उपयोग करता है। इसलिए, संभवतः यह केवल अधिक code execute करने के कारण Uvicorn से "धीमा" हो सकता है।
* लेकिन यह आपको simple web applications बनाने के लिए tools देता है, जैसे paths पर आधारित routing आदि।
* यदि आप Starlette की तुलना कर रहे हैं, तो इसकी तुलना Sanic, Flask, Django आदि से करें। Web frameworks (या microframeworks) से।
* **FastAPI**:
* जैसे Starlette, Uvicorn का उपयोग करता है और उससे तेज़ नहीं हो सकता, वैसे ही **FastAPI**, Starlette का उपयोग करता है, इसलिए यह उससे तेज़ नहीं हो सकता।
* FastAPI, Starlette के ऊपर और features प्रदान करता है। ऐसे features जिनकी आपको APIs बनाते समय लगभग हमेशा आवश्यकता होती है, जैसे data validation और serialization। और इसका उपयोग करके आपको automatic documentation मुफ्त में मिलती है (automatic documentation running applications में overhead भी नहीं जोड़ती, यह startup पर generate होती है)।
* यदि आपने FastAPI का उपयोग नहीं किया और सीधे Starlette (या कोई अन्य tool, जैसे Sanic, Flask, Responder आदि) का उपयोग किया, तो आपको सभी data validation और serialization स्वयं implement करने पड़ेंगे। इसलिए, आपके final application में फिर भी उतना ही overhead होगा जितना FastAPI का उपयोग करके बनाए जाने पर होता। और कई मामलों में, यह data validation और serialization applications में लिखे गए code का सबसे बड़ा हिस्सा होता है।
* इसलिए, FastAPI का उपयोग करके आप development time, bugs और lines of code बचाते हैं, और संभवतः आपको वही performance (या बेहतर) मिलेगी जो आपको इसे उपयोग न करने पर मिलती (क्योंकि तब आपको यह सब अपने code में implement करना पड़ता)।
* यदि आप FastAPI की तुलना कर रहे हैं, तो इसकी तुलना ऐसे web application framework (या tools के set) से करें जो data validation, serialization और documentation प्रदान करता हो, जैसे Flask-apispec, NestJS, Molten आदि। ऐसे frameworks जिनमें integrated automatic data validation, serialization और documentation हो।

View File

@@ -1,23 +0,0 @@
# एडिटर सपोर्ट { #editor-support }
आधिकारिक [FastAPI Extension](https://marketplace.visualstudio.com/items?itemName=FastAPILabs.fastapi-vscode) आपके FastAPI development workflow को *पाथ ऑपरेशन* discovery, navigation, साथ ही FastAPI Cloud deployment और live log streaming के साथ बेहतर बनाता है।
Extension के बारे में अधिक जानकारी के लिए, [GitHub repository](https://github.com/fastapi/fastapi-vscode) पर README देखें।
## सेटअप और इंस्टॉलेशन { #setup-and-installation }
**FastAPI Extension** [VS Code](https://code.visualstudio.com/) और [Cursor](https://www.cursor.com/) दोनों के लिए उपलब्ध है। इसे हर editor के Extensions panel से सीधे "FastAPI" खोजकर और **FastAPI Labs** द्वारा प्रकाशित extension चुनकर install किया जा सकता है। यह extension browser-based editors जैसे [vscode.dev](https://vscode.dev) और [github.dev](https://github.dev) में भी काम करता है।
### एप्लिकेशन डिस्कवरी { #application-discovery }
Default रूप से, extension आपके workspace में `FastAPI()` instantiate करने वाली files को scan करके FastAPI applications को अपने-आप discover करेगा। यदि auto-detection आपके project structure के लिए काम नहीं करता, तो आप `pyproject.toml` में `[tool.fastapi]` के माध्यम से या module notation (जैसे `myapp.main:app`) का उपयोग करके `fastapi.entryPoint` VS Code setting में entrypoint specify कर सकते हैं।
## फीचर्स { #features }
- **Path Operation Explorer** - आपके application में सभी <dfn title="रूट्स, एंडपॉइंट्स">*पाथ ऑपरेशन्स*</dfn> का sidebar tree view। किसी भी route या router definition पर जाने के लिए click करें।
- **Route Search** - <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>E</kbd> (macOS पर: <kbd>Cmd</kbd> + <kbd>Shift</kbd> + <kbd>E</kbd>) के साथ path, method, या name के आधार पर search करें।
- **CodeLens Navigation** - test client calls (जैसे `client.get('/items')`) के ऊपर clickable links, जो tests और implementation के बीच quick navigation के लिए matching *पाथ ऑपरेशन* पर ले जाते हैं।
- **Deploy to FastAPI Cloud** - आपकी app को [FastAPI Cloud](https://fastapicloud.com/) पर one-click deployment।
- **Stream Application Logs** - level filtering और text search के साथ आपके FastAPI Cloud-deployed application से real-time log streaming।
यदि आप extension के features से परिचित होना चाहते हैं, तो Command Palette (<kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd> या macOS पर: <kbd>Cmd</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd>) खोलकर और "Welcome: Open walkthrough..." चुनकर, फिर "Get started with FastAPI" walkthrough चुनकर extension walkthrough देख सकते हैं।

View File

@@ -1,134 +0,0 @@
# FastAPI CLI { #fastapi-cli }
**FastAPI <abbr title="command line interface - कमांड लाइन इंटरफ़ेस">CLI</abbr>** एक command line प्रोग्राम है जिसका उपयोग आप अपने FastAPI ऐप को serve करने, अपने FastAPI प्रोजेक्ट को manage करने, और भी बहुत कुछ करने के लिए कर सकते हैं।
जब आप FastAPI इंस्टॉल करते हैं (जैसे `pip install "fastapi[standard]"` के साथ), तो इसके साथ एक command line प्रोग्राम आता है जिसे आप terminal में चला सकते हैं।
डेवलपमेंट के लिए अपना FastAPI ऐप चलाने के लिए, आप `fastapi dev` command का उपयोग कर सकते हैं:
<div class="termy">
```console
$ <font color="#4E9A06">fastapi</font> dev
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting development server 🚀
Searching for package file structure from directories with
<font color="#3465A4">__init__.py</font> files
Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
<span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> 🐍 main.py
<span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> Importing the FastAPI app object from the module with the
following code:
<u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000/docs</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> tip </font></span> Running in development mode, for production use:
<b>fastapi run</b>
Logs:
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Will watch for changes in these directories:
<b>[</b><font color="#4E9A06">&apos;/home/user/code/awesomeapp&apos;</font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://127.0.0.1:8000</u></font> <b>(</b>Press CTRL+C to
quit<b>)</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started reloader process <b>[</b><font color="#34E2E2"><b>383138</b></font><b>]</b> using WatchFiles
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>383153</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
```
</div>
/// tip | सुझाव
प्रोडक्शन के लिए आप `fastapi dev` की जगह `fastapi run` का उपयोग करेंगे। 🚀
///
आंतरिक रूप से, **FastAPI CLI** [Uvicorn](https://www.uvicorn.dev) का उपयोग करता है, जो एक high-performance, production-ready, ASGI server है। 😎
`fastapi` CLI चलाने के लिए FastAPI ऐप को अपने-आप detect करने की कोशिश करेगा, यह मानते हुए कि यह `main.py` फ़ाइल में `app` नाम का object है (या कुछ अन्य variants में से कोई एक)।
लेकिन आप उपयोग किए जाने वाले ऐप को स्पष्ट रूप से configure कर सकते हैं।
## ऐप `entrypoint` को `pyproject.toml` में configure करें { #configure-the-app-entrypoint-in-pyproject-toml }
आप `pyproject.toml` फ़ाइल में यह configure कर सकते हैं कि आपका ऐप कहाँ स्थित है, जैसे:
```toml
[tool.fastapi]
entrypoint = "main:app"
```
वह `entrypoint` `fastapi` command को बताएगा कि उसे ऐप को इस तरह import करना चाहिए:
```python
from main import app
```
अगर आपका code इस तरह structured था:
```
.
├── backend
│   ├── main.py
│   ├── __init__.py
```
तो आप `entrypoint` को इस तरह set करेंगे:
```toml
[tool.fastapi]
entrypoint = "backend.main:app"
```
जो इसके बराबर होगा:
```python
from backend.main import app
```
### path के साथ या `--entrypoint` CLI option के साथ `fastapi dev` { #fastapi-dev-with-path-or-with-entrypoint-cli-option }
आप `fastapi dev` command को file path भी pass कर सकते हैं, और यह उपयोग किए जाने वाले FastAPI app object का अनुमान लगा लेगा:
```console
$ fastapi dev main.py
```
या, आप `fastapi dev` command को `--entrypoint` option भी pass कर सकते हैं:
```console
$ fastapi dev --entrypoint main:app
```
लेकिन आपको हर बार `fastapi` command call करते समय सही path\entrypoint pass करना याद रखना होगा।
इसके अतिरिक्त, अन्य tools इसे ढूँढने में सक्षम नहीं हो सकते हैं, उदाहरण के लिए [VS Code एक्सटेंशन](editor-support.md) या [FastAPI Cloud](https://fastapicloud.com), इसलिए `pyproject.toml` में `entrypoint` का उपयोग करने की सिफारिश की जाती है।
## `fastapi dev` { #fastapi-dev }
`fastapi dev` चलाने से development mode शुरू होता है।
Default रूप से, **auto-reload** enabled होता है, जो आपके code में changes करने पर server को अपने-आप reload कर देता है। यह resource-intensive है और disabled होने की तुलना में कम stable हो सकता है। आपको इसे केवल development के लिए ही उपयोग करना चाहिए। यह IP address `127.0.0.1` पर भी listen करता है, जो आपकी machine का खुद से ही communicate करने वाला IP है (`localhost`)।
## `fastapi run` { #fastapi-run }
`fastapi run` execute करने से FastAPI production mode में शुरू होता है।
Default रूप से, **auto-reload** disabled होता है। यह IP address `0.0.0.0` पर भी listen करता है, जिसका मतलब सभी available IP addresses है, इस तरह यह उस machine से communicate कर सकने वाले किसी भी व्यक्ति के लिए publicly accessible होगा। आम तौर पर आप production में इसे इसी तरह चलाएँगे, उदाहरण के लिए, एक container में।
अधिकतर मामलों में आपके पास ऊपर से HTTPS handle करने वाला "termination proxy" होगा (और होना चाहिए), यह इस पर निर्भर करेगा कि आप अपना application कैसे deploy करते हैं, आपका provider आपके लिए यह कर सकता है, या आपको इसे खुद set up करने की ज़रूरत हो सकती है।
/// tip | सुझाव
आप इसके बारे में [deployment documentation](deployment/index.md) में और जान सकते हैं।
///

View File

@@ -1,201 +0,0 @@
# विशेषताएँ { #features }
## FastAPI की विशेषताएँ { #fastapi-features }
**FastAPI** आपको निम्नलिखित देता है:
### खुले मानकों पर आधारित { #based-on-open-standards }
* API बनाने के लिए [**OpenAPI**](https://github.com/OAI/OpenAPI-Specification), जिसमें <dfn title="इन्हें भी कहा जाता है: endpoints, routes">पाथ</dfn> <dfn title="इन्हें HTTP methods भी कहा जाता है, जैसे POST, GET, PUT, DELETE">ऑपरेशन्स</dfn>, parameters, request bodies, security, आदि की घोषणाएँ शामिल हैं।
* [**JSON Schema**](https://json-schema.org/) के साथ automatic data model documentation (क्योंकि OpenAPI स्वयं JSON Schema पर आधारित है)।
* इन मानकों के इर्द-गिर्द डिज़ाइन किया गया, एक बहुत सावधानीपूर्वक अध्ययन के बाद। ऊपर से बाद में जोड़ी गई परत की तरह नहीं।
* यह कई भाषाओं में automatic **client code generation** का उपयोग करने की भी अनुमति देता है।
### Automatic docs { #automatic-docs }
Interactive API documentation और exploration web user interfaces। क्योंकि framework OpenAPI पर आधारित है, कई विकल्प हैं, जिनमें 2 default रूप से शामिल हैं।
* [**Swagger UI**](https://github.com/swagger-api/swagger-ui), interactive exploration के साथ, अपने API को सीधे browser से call और test करें।
![Swagger UI इंटरैक्शन](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png)
* [**ReDoc**](https://github.com/Rebilly/ReDoc) के साथ वैकल्पिक API documentation।
![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png)
### सिर्फ़ आधुनिक Python { #just-modern-python }
यह सब standard **Python type** declarations पर आधारित है (Pydantic की बदौलत)। सीखने के लिए कोई नया syntax नहीं। सिर्फ़ standard modern Python।
अगर आपको Python types का उपयोग कैसे करें, इसका 2 मिनट का छोटा refresher चाहिए (भले ही आप FastAPI का उपयोग न करते हों), तो छोटा tutorial देखें: [Python Types](python-types.md)।
आप types के साथ standard Python लिखते हैं:
```Python
from datetime import date
from pydantic import BaseModel
# किसी वेरिएबल को str के रूप में घोषित करें
# और फ़ंक्शन के अंदर एडिटर सपोर्ट पाएँ
def main(user_id: str):
return user_id
# एक Pydantic मॉडल
class User(BaseModel):
id: int
name: str
joined: date
```
फिर उसे इस तरह उपयोग किया जा सकता है:
```Python
my_user: User = User(id=3, name="John Doe", joined="2018-07-19")
second_user_data = {
"id": 4,
"name": "Mary",
"joined": "2018-11-30",
}
my_second_user: User = User(**second_user_data)
```
/// note | नोट
`**second_user_data` का मतलब है:
`second_user_data` dict की keys और values को सीधे key-value arguments के रूप में पास करें, जो इसके बराबर है: `User(id=4, name="Mary", joined="2018-11-30")`
///
### एडिटर सपोर्ट { #editor-support }
पूरे framework को उपयोग में आसान और सहज बनाने के लिए डिज़ाइन किया गया था, विकास शुरू करने से पहले ही सभी निर्णयों को कई editors पर test किया गया, ताकि सबसे अच्छा development experience सुनिश्चित किया जा सके।
Python developer surveys में, यह स्पष्ट है [कि सबसे अधिक उपयोग की जाने वाली सुविधाओं में से एक "autocompletion" है](https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features)।
पूरा **FastAPI** framework इसे पूरा करने के लिए डिज़ाइन किया गया है। Autocompletion हर जगह काम करता है।
आपको शायद ही कभी docs पर वापस आने की आवश्यकता होगी।
यहाँ बताया गया है कि आपका editor आपकी कैसे मदद कर सकता है:
* [Visual Studio Code](https://code.visualstudio.com/) में:
![एडिटर सपोर्ट](https://fastapi.tiangolo.com/img/vscode-completion.png)
* [PyCharm](https://www.jetbrains.com/pycharm/) में:
![एडिटर सपोर्ट](https://fastapi.tiangolo.com/img/pycharm-completion.png)
आपको ऐसे code में completion मिलेगा जिसे आप पहले असंभव भी मान सकते थे। जैसे, किसी request से आने वाले JSON body (जो nested भी हो सकता था) के अंदर `price` key।
अब गलत key names टाइप करने, docs के बीच आगे-पीछे जाने, या यह खोजने के लिए ऊपर-नीचे scroll करने की ज़रूरत नहीं कि आपने आखिर `username` उपयोग किया था या `user_name`
### संक्षिप्त { #short }
हर चीज़ के लिए इसके समझदार **defaults** हैं, और हर जगह optional configurations हैं। सभी parameters को आपकी ज़रूरत के अनुसार और आपकी ज़रूरत की API define करने के लिए fine-tune किया जा सकता है।
लेकिन default रूप से, सब कुछ **"बस काम करता है"**।
### Validation { #validation }
* अधिकांश (या सभी?) Python **data types** के लिए validation, जिनमें शामिल हैं:
* JSON objects (`dict`)।
* JSON array (`list`) जो item types define करता है।
* String (`str`) fields, जिनमें min और max lengths define होती हैं।
* Numbers (`int`, `float`) जिनमें min और max values, आदि।
* अधिक असामान्य types के लिए validation, जैसे:
* URL।
* Email।
* UUID।
* ...और अन्य।
सारी validation well-established और robust **Pydantic** द्वारा handle की जाती है।
### Security और authentication { #security-and-authentication }
Security और authentication integrated हैं। Databases या data models के साथ किसी भी compromise के बिना।
OpenAPI में define की गई सभी security schemes, जिनमें शामिल हैं:
* HTTP Basic।
* **OAuth2** (**JWT tokens** के साथ भी)। [OAuth2 with JWT](tutorial/security/oauth2-jwt.md) पर tutorial देखें।
* API keys:
* Headers में।
* Query parameters में।
* Cookies में, आदि।
साथ ही Starlette की सभी security features (जिसमें **session cookies** भी शामिल हैं)।
सब reusable tools और components के रूप में बनाए गए हैं, जिन्हें आपके systems, data stores, relational और NoSQL databases, आदि के साथ integrate करना आसान है।
### Dependency Injection { #dependency-injection }
FastAPI में एक बेहद आसान, लेकिन बेहद शक्तिशाली <dfn title='इन्हें "components", "resources", "services", "providers" भी कहा जाता है'><strong>Dependency Injection</strong></dfn> system शामिल है।
* Dependencies की भी dependencies हो सकती हैं, जिससे dependencies की hierarchy या **"graph"** बनता है।
* सब कुछ framework द्वारा **automatically handled** होता है।
* सभी dependencies requests से data मांग सकती हैं और **path operation** constraints तथा automatic documentation को बढ़ा सकती हैं।
* Dependencies में define किए गए *path operation* parameters के लिए भी **automatic validation**
* जटिल user authentication systems, **database connections**, आदि के लिए support।
* Databases, frontends, आदि के साथ **कोई compromise नहीं**। लेकिन उन सभी के साथ आसान integration।
### असीमित "plug-ins" { #unlimited-plug-ins }
या दूसरे शब्दों में, उनकी आवश्यकता नहीं है, अपनी ज़रूरत का code import करें और उपयोग करें।
किसी भी integration को (dependencies के साथ) उपयोग में इतना सरल बनाने के लिए डिज़ाइन किया गया है कि आप अपनी application के लिए उसी structure और syntax का उपयोग करके 2 lines of code में एक "plug-in" बना सकते हैं, जो आपके *path operations* के लिए उपयोग होता है।
### Tested { #tested }
* 100% <dfn title="code की वह मात्रा जो automatically test की जाती है">test coverage</dfn>।
* 100% <dfn title="Python type annotations, जिनसे आपका editor और external tools आपको बेहतर support दे सकते हैं">type annotated</dfn> code base।
* Production applications में उपयोग किया गया।
## Starlette की विशेषताएँ { #starlette-features }
**FastAPI** [**Starlette**](https://www.starlette.dev/) के साथ पूरी तरह compatible है (और उसी पर आधारित है)। इसलिए, आपके पास जो भी अतिरिक्त Starlette code है, वह भी काम करेगा।
`FastAPI` वास्तव में `Starlette` का एक sub-class है। इसलिए, अगर आप पहले से Starlette जानते हैं या उपयोग करते हैं, तो अधिकांश functionality उसी तरह काम करेगी।
**FastAPI** के साथ आपको **Starlette** की सभी features मिलती हैं (क्योंकि FastAPI मूलतः steroids पर Starlette है):
* सचमुच प्रभावशाली performance। यह [उपलब्ध सबसे तेज़ Python frameworks में से एक है, **NodeJS** और **Go** के बराबर](https://github.com/encode/starlette#performance)।
* **WebSocket** support।
* In-process background tasks।
* Startup और shutdown events।
* HTTPX पर बना test client।
* **CORS**, GZip, Static Files, Streaming responses।
* **Session और Cookie** support।
* 100% test coverage।
* 100% type annotated codebase।
## Pydantic की विशेषताएँ { #pydantic-features }
**FastAPI** [**Pydantic**](https://docs.pydantic.dev/) के साथ पूरी तरह compatible है (और उसी पर आधारित है)। इसलिए, आपके पास जो भी अतिरिक्त Pydantic code है, वह भी काम करेगा।
इसमें Pydantic पर आधारित external libraries भी शामिल हैं, जैसे databases के लिए <abbr title="Object-Relational Mapper - ऑब्जेक्ट-रिलेशनल मैपर">ORM</abbr>s और <abbr title="Object-Document Mapper - ऑब्जेक्ट-डॉक्यूमेंट मैपर">ODM</abbr>s।
इसका यह भी मतलब है कि कई मामलों में आप request से मिलने वाले उसी object को **सीधे database में** पास कर सकते हैं, क्योंकि सब कुछ automatically validated होता है।
उसी तरह उल्टा भी लागू होता है, कई मामलों में आप database से मिलने वाले object को **सीधे client को** पास कर सकते हैं।
**FastAPI** के साथ आपको **Pydantic** की सभी features मिलती हैं (क्योंकि FastAPI सभी data handling के लिए Pydantic पर आधारित है):
* **कोई brainfuck नहीं**:
* सीखने के लिए कोई नई schema definition micro-language नहीं।
* अगर आप Python types जानते हैं, तो आप जानते हैं कि Pydantic का उपयोग कैसे करना है।
* आपके **<abbr title="Integrated Development Environment - इंटीग्रेटेड डेवलपमेंट एनवायरनमेंट: code editor जैसा">IDE</abbr>/<dfn title="एक program जो code errors की जाँच करता है">लिंटर</dfn>/brain** के साथ अच्छी तरह काम करता है:
* क्योंकि pydantic data structures केवल उन classes के instances होते हैं जिन्हें आप define करते हैं; auto-completion, linting, mypy और आपकी intuition, सभी आपके validated data के साथ सही ढंग से काम करने चाहिए।
* **Complex structures** validate करें:
* Hierarchical Pydantic models, Python `typing` के `List` और `Dict`, आदि का उपयोग।
* और validators complex data schemas को JSON Schema के रूप में स्पष्ट और आसानी से define, check और document करने देते हैं।
* आपके पास deeply **nested JSON** objects हो सकते हैं और वे सभी validated और annotated हो सकते हैं।
* **Extensible**:
* Pydantic custom data types को define करने देता है या आप validator decorator से decorated model पर methods के साथ validation extend कर सकते हैं।
* 100% test coverage।

View File

@@ -1,79 +0,0 @@
# इतिहास, डिज़ाइन और भविष्य { #history-design-and-future }
कुछ समय पहले, [एक **FastAPI** उपयोगकर्ता ने पूछा](https://github.com/fastapi/fastapi/issues/3#issuecomment-454956920):
> इस प्रोजेक्ट का इतिहास क्या है? ऐसा लगता है कि यह कुछ ही हफ्तों में कहीं से भी सीधे शानदार बन गया [...]
यहाँ उस इतिहास का एक छोटा सा हिस्सा है।
## विकल्प { #alternatives }
मैं कई वर्षों से जटिल आवश्यकताओं वाली APIs बना रहा हूँ (Machine Learning, distributed systems, asynchronous jobs, NoSQL databases, आदि), और डेवलपर्स की कई टीमों का नेतृत्व कर चुका हूँ।
इसके हिस्से के रूप में, मुझे कई विकल्पों की जाँच, परीक्षण और उपयोग करना पड़ा।
**FastAPI** का इतिहास काफी हद तक इसके पूर्ववर्तियों का इतिहास है।
जैसा कि [विकल्प](alternatives.md) सेक्शन में कहा गया है:
<blockquote markdown="1">
दूसरों के पिछले काम के बिना **FastAPI** मौजूद नहीं होता।
इससे पहले कई टूल बनाए गए हैं जिन्होंने इसके निर्माण को प्रेरित करने में मदद की है।
मैं कई वर्षों से एक नया framework बनाने से बचता रहा। पहले मैंने **FastAPI** द्वारा कवर किए गए सभी features को कई अलग-अलग frameworks, plug-ins और tools का उपयोग करके हल करने की कोशिश की।
लेकिन किसी बिंदु पर, ऐसा कुछ बनाने के अलावा कोई विकल्प नहीं बचा था जो ये सभी features प्रदान करे, पिछले tools से सर्वोत्तम ideas ले और उन्हें सर्वोत्तम संभव तरीके से जोड़े, साथ ही ऐसी language features का उपयोग करे जो पहले उपलब्ध भी नहीं थीं (Python 3.6+ type hints)।
</blockquote>
## जाँच-पड़ताल { #investigation }
सभी पिछले विकल्पों का उपयोग करके मुझे उन सभी से सीखने, ideas लेने, और उन्हें अपने तथा जिन डेवलपर टीमों के साथ मैंने काम किया है उनके लिए सबसे अच्छे तरीके से मिलाने का अवसर मिला।
उदाहरण के लिए, यह स्पष्ट था कि आदर्श रूप से इसे standard Python type hints पर आधारित होना चाहिए।
साथ ही, सबसे अच्छा तरीका पहले से मौजूद standards का उपयोग करना था।
इसलिए, **FastAPI** की coding शुरू करने से पहले ही, मैंने OpenAPI, JSON Schema, OAuth2 आदि के specs का अध्ययन करने में कई महीने बिताए। उनके संबंध, overlap और differences को समझा।
## डिज़ाइन { #design }
फिर मैंने उस developer "API" को डिज़ाइन करने में कुछ समय लगाया जिसे मैं एक user के रूप में पाना चाहता था (FastAPI का उपयोग करने वाले developer के रूप में)।
मैंने सबसे लोकप्रिय Python editors में कई ideas का परीक्षण किया: PyCharm, VS Code, Jedi आधारित editors।
पिछले [Python Developer Survey](https://www.jetbrains.com/research/python-developers-survey-2018/#development-tools) के अनुसार, यह लगभग 80% users को कवर करता है।
इसका मतलब है कि **FastAPI** को विशेष रूप से उन editors के साथ test किया गया था जिनका उपयोग 80% Python developers करते हैं। और चूँकि अधिकांश अन्य editors भी समान तरीके से काम करते हैं, इसके सभी लाभ लगभग सभी editors के लिए काम करने चाहिए।
इस तरह मैं code duplication को जितना संभव हो उतना कम करने, हर जगह completion पाने, type और error checks आदि के सर्वोत्तम तरीके खोज सका।
सब कुछ इस तरह से किया गया कि सभी developers को सर्वोत्तम development experience मिल सके।
## आवश्यकताएँ { #requirements }
कई विकल्पों का परीक्षण करने के बाद, मैंने तय किया कि मैं इसके लाभों के लिए [**Pydantic**](https://docs.pydantic.dev/) का उपयोग करूँगा।
फिर मैंने इसमें योगदान दिया, ताकि इसे JSON Schema के साथ पूरी तरह compliant बनाया जा सके, constraint declarations को define करने के अलग-अलग तरीकों का समर्थन किया जा सके, और कई editors में tests के आधार पर editor support (type checks, autocompletion) को बेहतर बनाया जा सके।
Development के दौरान, मैंने [**Starlette**](https://www.starlette.dev/) में भी योगदान दिया, जो दूसरी मुख्य आवश्यकता थी।
## Development { #development }
जब तक मैंने **FastAPI** खुद बनाना शुरू किया, तब तक अधिकांश हिस्से पहले से तैयार थे, design तय हो चुका था, requirements और tools तैयार थे, और standards तथा specifications के बारे में ज्ञान स्पष्ट और ताज़ा था।
## भविष्य { #future }
इस बिंदु तक, यह पहले से ही स्पष्ट है कि **FastAPI** अपने ideas के साथ कई लोगों के लिए उपयोगी साबित हो रहा है।
इसे कई use cases के लिए बेहतर उपयुक्त होने के कारण पिछले विकल्पों पर चुना जा रहा है।
कई developers और teams अपने projects के लिए पहले से ही **FastAPI** पर निर्भर हैं (मेरे और मेरी team सहित)।
लेकिन फिर भी, अभी कई improvements और features आने बाकी हैं।
**FastAPI** का भविष्य बहुत उज्ज्वल है।
और [आपकी मदद](help-fastapi.md) की बहुत सराहना की जाती है।

View File

@@ -1,28 +0,0 @@
# Full Stack FastAPI Template { #full-stack-fastapi-template }
Templates आम तौर पर एक विशिष्ट setup के साथ आते हैं, लेकिन उन्हें flexible और customizable होने के लिए डिज़ाइन किया जाता है। इससे आप उन्हें अपने project की आवश्यकताओं के अनुसार modify और adapt कर सकते हैं, जिससे वे एक बेहतरीन starting point बन जाते हैं। 🏁
आप शुरू करने के लिए इस template का उपयोग कर सकते हैं, क्योंकि इसमें आपके लिए बहुत सा initial setup, security, database और कुछ API endpoints पहले से तैयार हैं।
GitHub Repository: [Full Stack FastAPI Template](https://github.com/tiangolo/full-stack-fastapi-template)
## Full Stack FastAPI Template - Technology Stack और Features { #full-stack-fastapi-template-technology-stack-and-features }
- ⚡ Python backend API के लिए [**FastAPI**](https://fastapi.tiangolo.com/hi)।
- 🧰 Python SQL database interactions (ORM) के लिए [SQLModel](https://sqlmodel.tiangolo.com)।
- 🔍 data validation और settings management के लिए [Pydantic](https://docs.pydantic.dev), जिसका उपयोग FastAPI करता है।
- 💾 SQL database के रूप में [PostgreSQL](https://www.postgresql.org)।
- 🚀 frontend के लिए [React](https://react.dev)।
- 💃 TypeScript, hooks, Vite, और modern frontend stack के अन्य parts का उपयोग।
- 🎨 frontend components के लिए [Tailwind CSS](https://tailwindcss.com) और [shadcn/ui](https://ui.shadcn.com)।
- 🤖 एक automatically generated frontend client।
- 🧪 End-to-End testing के लिए [Playwright](https://playwright.dev)।
- 🦇 Dark mode support।
- 🐋 development और production के लिए [Docker Compose](https://www.docker.com)।
- 🔒 default रूप से secure password hashing।
- 🔑 JWT (JSON Web Token) authentication।
- 📫 Email आधारित password recovery।
- ✅ [Pytest](https://pytest.org) के साथ tests।
- 📞 reverse proxy / load balancer के रूप में [Traefik](https://traefik.io)।
- 🚢 Docker Compose का उपयोग करके deployment instructions, जिसमें automatic HTTPS certificates handle करने के लिए frontend Traefik proxy setup करना शामिल है।
- 🏭 GitHub Actions पर आधारित CI (continuous integration) और CD (continuous deployment)।

View File

@@ -1,348 +0,0 @@
# Python Types परिचय { #python-types-intro }
Python में वैकल्पिक "type hints" (जिन्हें "type annotations" भी कहा जाता है) का समर्थन है।
ये **"type hints"** या annotations एक विशेष syntax हैं, जो किसी variable का <dfn title="उदाहरण के लिए: str, int, float, bool">type</dfn> घोषित करने की अनुमति देते हैं।
अपने variables के लिए types घोषित करके, editors और tools आपको बेहतर support दे सकते हैं।
यह Python type hints के बारे में बस एक **त्वरित tutorial / refresher** है। इसमें केवल उतना ही शामिल है जितना उन्हें **FastAPI** के साथ उपयोग करने के लिए न्यूनतम रूप से आवश्यक है... जो वास्तव में बहुत कम है।
**FastAPI** पूरी तरह से इन्हीं type hints पर आधारित है, ये इसे कई फायदे और लाभ देते हैं।
लेकिन अगर आप कभी **FastAPI** का उपयोग नहीं भी करते, तब भी इनके बारे में थोड़ा सीखने से आपको लाभ होगा।
/// note | नोट
अगर आप Python expert हैं, और type hints के बारे में पहले से सब कुछ जानते हैं, तो अगले chapter पर जाएँ।
///
## प्रेरणा { #motivation }
आइए एक सरल उदाहरण से शुरू करें:
{* ../../docs_src/python_types/tutorial001_py310.py *}
इस program को call करने पर output आता है:
```
John Doe
```
Function निम्नलिखित करता है:
* एक `first_name` और `last_name` लेता है।
* प्रत्येक के पहले अक्षर को `title()` के साथ upper case में बदलता है।
* उन्हें बीच में एक space के साथ <dfn title="उन्हें एक साथ रखता है, एक के रूप में। एक की सामग्री के बाद दूसरे की सामग्री के साथ।">Concatenate</dfn> करता है।
{* ../../docs_src/python_types/tutorial001_py310.py hl[2] *}
### इसे edit करें { #edit-it }
यह एक बहुत सरल program है।
लेकिन अब कल्पना करें कि आप इसे scratch से लिख रहे थे।
किसी point पर आप function define करना शुरू करते हैं, और आपके parameters तैयार हैं...
लेकिन फिर आपको "वह method जो पहले अक्षर को upper case में बदलता है" call करना है।
क्या वह `upper` था? क्या वह `uppercase` था? `first_uppercase`? `capitalize`?
फिर, आप programmer के पुराने दोस्त, editor autocompletion के साथ कोशिश करते हैं।
आप function का पहला parameter, `first_name`, फिर एक dot (`.`) type करते हैं और फिर completion trigger करने के लिए `Ctrl+Space` दबाते हैं।
लेकिन, दुख की बात है, आपको कुछ भी उपयोगी नहीं मिलता:
<img src="/img/python-types/image01.png">
### Types जोड़ें { #add-types }
आइए पिछले version की एक single line बदलते हैं।
हम ठीक इस fragment को, function के parameters को, इससे बदलेंगे:
```Python
first_name, last_name
```
इसमें:
```Python
first_name: str, last_name: str
```
बस इतना ही।
यही "type hints" हैं:
{* ../../docs_src/python_types/tutorial002_py310.py hl[1] *}
यह default values declare करने जैसा नहीं है, जैसा कि इसमें होता:
```Python
first_name="john", last_name="doe"
```
यह एक अलग चीज़ है।
हम colons (`:`) का उपयोग कर रहे हैं, equals (`=`) का नहीं।
और type hints जोड़ने से सामान्यतः यह नहीं बदलता कि बिना उनके जो होता, वह कैसे होता।
लेकिन अब, कल्पना करें कि आप फिर से उस function को बनाने के बीच में हैं, लेकिन type hints के साथ।
उसी point पर, आप `Ctrl+Space` के साथ autocomplete trigger करने की कोशिश करते हैं और आप देखते हैं:
<img src="/img/python-types/image02.png">
इसके साथ, आप options देखते हुए scroll कर सकते हैं, जब तक आपको वह न मिल जाए जो "पहचाना हुआ लगे":
<img src="/img/python-types/image03.png">
## और प्रेरणा { #more-motivation }
इस function को देखें, इसमें पहले से type hints हैं:
{* ../../docs_src/python_types/tutorial003_py310.py hl[1] *}
क्योंकि editor variables के types जानता है, आपको केवल completion ही नहीं मिलता, आपको error checks भी मिलते हैं:
<img src="/img/python-types/image04.png">
अब आप जानते हैं कि आपको इसे ठीक करना है, `age` को `str(age)` के साथ string में convert करना है:
{* ../../docs_src/python_types/tutorial004_py310.py hl[2] *}
## Types declare करना { #declaring-types }
आपने अभी type hints declare करने की मुख्य जगह देखी। Function parameters के रूप में।
यही वह मुख्य जगह भी है जहाँ आप उन्हें **FastAPI** के साथ उपयोग करेंगे।
### Simple types { #simple-types }
आप सभी standard Python types declare कर सकते हैं, केवल `str` ही नहीं।
आप उदाहरण के लिए उपयोग कर सकते हैं:
* `int`
* `float`
* `bool`
* `bytes`
{* ../../docs_src/python_types/tutorial005_py310.py hl[1] *}
### `typing` module { #typing-module }
कुछ अतिरिक्त use cases के लिए, आपको standard library के `typing` module से कुछ चीज़ें import करने की आवश्यकता हो सकती है, उदाहरण के लिए जब आप declare करना चाहते हैं कि किसी चीज़ का "कोई भी type" है, तो आप `typing` से `Any` का उपयोग कर सकते हैं:
```python
from typing import Any
def some_function(data: Any):
print(data)
```
### Generic types { #generic-types }
कुछ types square brackets में "type parameters" ले सकते हैं, ताकि उनके internal types define किए जा सकें, उदाहरण के लिए "strings की list" को `list[str]` declare किया जाएगा।
जो types type parameters ले सकते हैं उन्हें **Generic types** या **Generics** कहा जाता है।
आप उन्हीं builtin types को generics के रूप में उपयोग कर सकते हैं (square brackets और अंदर types के साथ):
* `list`
* `tuple`
* `set`
* `dict`
#### List { #list }
उदाहरण के लिए, आइए एक variable को `str` की `list` के रूप में define करते हैं।
Variable को उसी colon (`:`) syntax के साथ declare करें।
Type के रूप में, `list` रखें।
क्योंकि list एक ऐसा type है जिसमें कुछ internal types होते हैं, आप उन्हें square brackets में रखते हैं:
{* ../../docs_src/python_types/tutorial006_py310.py hl[1] *}
/// note | नोट
Square brackets में मौजूद उन internal types को "type parameters" कहा जाता है।
इस case में, `str` वह type parameter है जो `list` को pass किया गया है।
///
इसका मतलब है: "variable `items` एक `list` है, और इस list का प्रत्येक item एक `str` है"।
ऐसा करने से, आपका editor list से items process करते समय भी support दे सकता है:
<img src="/img/python-types/image05.png">
Types के बिना, इसे हासिल करना लगभग असंभव है।
ध्यान दें कि variable `item`, list `items` के elements में से एक है।
और फिर भी, editor जानता है कि यह एक `str` है, और उसके लिए support देता है।
#### Tuple और Set { #tuple-and-set }
आप `tuple`s और `set`s declare करने के लिए भी ऐसा ही करेंगे:
{* ../../docs_src/python_types/tutorial007_py310.py hl[1] *}
इसका मतलब है:
* Variable `items_t` 3 items वाला एक `tuple` है, एक `int`, दूसरा `int`, और एक `str`
* Variable `items_s` एक `set` है, और इसके प्रत्येक item का type `bytes` है।
#### Dict { #dict }
`dict` define करने के लिए, आप 2 type parameters pass करते हैं, commas से separated।
पहला type parameter `dict` की keys के लिए होता है।
दूसरा type parameter `dict` की values के लिए होता है:
{* ../../docs_src/python_types/tutorial008_py310.py hl[1] *}
इसका मतलब है:
* Variable `prices` एक `dict` है:
* इस `dict` की keys `str` type की हैं (मान लें, प्रत्येक item का नाम)।
* इस `dict` की values `float` type की हैं (मान लें, प्रत्येक item की price)।
#### Union { #union }
आप declare कर सकते हैं कि कोई variable **कई types** में से कोई भी हो सकता है, उदाहरण के लिए, एक `int` या एक `str`
इसे define करने के लिए आप दोनों types को separate करने के लिए <dfn title='इसे "bitwise or operator" भी कहा जाता है, लेकिन वह अर्थ यहाँ relevant नहीं है'>vertical bar (`|`)</dfn> का उपयोग करते हैं।
इसे "union" कहा जाता है, क्योंकि variable उन दो type sets के union में कुछ भी हो सकता है।
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
```
इसका मतलब है कि `item` एक `int` या एक `str` हो सकता है।
#### संभवतः `None` { #possibly-none }
आप declare कर सकते हैं कि किसी value का type, जैसे `str`, हो सकता है, लेकिन वह `None` भी हो सकती है।
//// tab | Python 3.10+
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial009_py310.py!}
```
////
सिर्फ `str` के बजाय `str | None` का उपयोग करने से editor आपको उन errors को detect करने में मदद करेगा जहाँ आप यह मान रहे हो सकते हैं कि कोई value हमेशा `str` है, जबकि वास्तव में वह `None` भी हो सकती है।
### Classes को types के रूप में { #classes-as-types }
आप किसी class को भी variable के type के रूप में declare कर सकते हैं।
मान लें आपके पास एक class `Person` है, जिसमें एक name है:
{* ../../docs_src/python_types/tutorial010_py310.py hl[1:3] *}
फिर आप किसी variable को `Person` type का declare कर सकते हैं:
{* ../../docs_src/python_types/tutorial010_py310.py hl[6] *}
और फिर, फिर से, आपको पूरा editor support मिलता है:
<img src="/img/python-types/image06.png">
ध्यान दें कि इसका मतलब है "`one_person`, class `Person` का एक **instance** है"।
इसका मतलब यह नहीं है कि "`one_person`, `Person` नाम की **class** है"।
## Pydantic models { #pydantic-models }
[Pydantic](https://docs.pydantic.dev/) data validation करने के लिए एक Python library है।
आप data की "shape" को attributes वाली classes के रूप में declare करते हैं।
और प्रत्येक attribute का एक type होता है।
फिर आप कुछ values के साथ उस class का एक instance create करते हैं और यह values को validate करेगा, उन्हें appropriate type में convert करेगा (अगर ऐसा case है) और आपको पूरे data वाला एक object देगा।
और उस resulting object के साथ आपको पूरा editor support मिलता है।
Official Pydantic docs से एक उदाहरण:
{* ../../docs_src/python_types/tutorial011_py310.py *}
/// note | नोट
[Pydantic के बारे में अधिक जानने के लिए, इसके docs देखें](https://docs.pydantic.dev/)।
///
**FastAPI** पूरी तरह से Pydantic पर आधारित है।
आप यह सब practice में [Tutorial - User Guide](tutorial/index.md) में बहुत अधिक देखेंगे।
## Metadata Annotations के साथ Type Hints { #type-hints-with-metadata-annotations }
Python में एक feature भी है जो `Annotated` का उपयोग करके इन type hints में **अतिरिक्त <dfn title="Data के बारे में data, इस case में, type के बारे में जानकारी, जैसे description।">metadata</dfn>** डालने की अनुमति देता है।
आप `typing` से `Annotated` import कर सकते हैं।
{* ../../docs_src/python_types/tutorial013_py310.py hl[1,4] *}
Python खुद इस `Annotated` के साथ कुछ नहीं करता। और editors और अन्य tools के लिए, type अभी भी `str` है।
लेकिन आप `Annotated` में इस जगह का उपयोग **FastAPI** को अतिरिक्त metadata देने के लिए कर सकते हैं कि आप अपनी application को कैसे behave कराना चाहते हैं।
याद रखने वाली महत्वपूर्ण बात यह है कि `Annotated` को pass किया गया **पहला *type parameter*** ही **actual type** होता है। बाकी सब, अन्य tools के लिए केवल metadata है।
अभी के लिए, आपको बस यह जानना है कि `Annotated` मौजूद है, और यह standard Python है। 😎
बाद में आप देखेंगे कि यह कितना **powerful** हो सकता है।
/// tip | सुझाव
यह तथ्य कि यह **standard Python** है, इसका मतलब है कि आपको अपने editor में, अपने code को analyze और refactor करने वाले tools के साथ, आदि, अभी भी **सबसे अच्छा possible developer experience** मिलेगा। ✨
और यह भी कि आपका code कई अन्य Python tools और libraries के साथ बहुत compatible होगा। 🚀
///
## **FastAPI** में Type hints { #type-hints-in-fastapi }
**FastAPI** इन type hints का लाभ उठाकर कई चीज़ें करता है।
**FastAPI** के साथ आप type hints के साथ parameters declare करते हैं और आपको मिलता है:
* **Editor support**।
* **Type checks**।
...और **FastAPI** उन्हीं declarations का उपयोग करता है:
* **Requirements define** करने के लिए: request path parameters, query parameters, headers, bodies, dependencies, आदि से।
* **Data convert** करने के लिए: request से required type में।
* **Data validate** करने के लिए: प्रत्येक request से आने वाले data को:
* Data invalid होने पर client को लौटाए जाने वाले **automatic errors** generate करना।
* OpenAPI का उपयोग करके API को **document** करने के लिए:
* जिसका उपयोग फिर automatic interactive documentation user interfaces द्वारा किया जाता है।
यह सब abstract लग सकता है। चिंता न करें। आप यह सब action में [Tutorial - User Guide](tutorial/index.md) में देखेंगे।
महत्वपूर्ण बात यह है कि standard Python types का उपयोग करके, एक ही जगह पर (अधिक classes, decorators, आदि जोड़ने के बजाय), **FastAPI** आपके लिए बहुत सारा काम कर देगा।
/// note | नोट
अगर आप पहले ही पूरे tutorial से गुजर चुके हैं और types के बारे में और देखने के लिए वापस आए हैं, तो एक अच्छा resource [`mypy` की "cheat sheet"](https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html) है।
///

View File

@@ -1,862 +0,0 @@
# वर्चुअल एनवायरनमेंट { #virtual-environments }
जब आप Python प्रोजेक्ट्स पर काम करते हैं, तो संभवतः आपको हर प्रोजेक्ट के लिए इंस्टॉल किए जाने वाले पैकेजों को अलग रखने के लिए एक **वर्चुअल एनवायरनमेंट** (या कोई समान तरीका) इस्तेमाल करना चाहिए।
/// note | नोट
अगर आप पहले से वर्चुअल एनवायरनमेंट्स के बारे में जानते हैं, उन्हें कैसे बनाना और इस्तेमाल करना है जानते हैं, तो आप इस सेक्शन को छोड़ना चाह सकते हैं। 🤓
///
/// tip | सुझाव
एक **वर्चुअल एनवायरनमेंट**, एक **एनवायरनमेंट वेरिएबल** से अलग होता है।
एक **एनवायरनमेंट वेरिएबल** सिस्टम में एक वेरिएबल होता है जिसे प्रोग्राम इस्तेमाल कर सकते हैं।
एक **वर्चुअल एनवायरनमेंट** एक डायरेक्टरी होती है जिसमें कुछ फाइलें होती हैं।
///
/// note | नोट
यह पेज आपको **वर्चुअल एनवायरनमेंट्स** का उपयोग करना और वे कैसे काम करते हैं, सिखाएगा।
अगर आप अपने लिए **सब कुछ मैनेज करने वाला टूल** अपनाने के लिए तैयार हैं (जिसमें Python इंस्टॉल करना भी शामिल है), तो [uv](https://github.com/astral-sh/uv) आज़माएँ।
///
## एक प्रोजेक्ट बनाएँ { #create-a-project }
सबसे पहले, अपने प्रोजेक्ट के लिए एक डायरेक्टरी बनाएँ।
मैं सामान्यतः अपनी home/user डायरेक्टरी के अंदर `code` नाम की एक डायरेक्टरी बनाता हूँ।
और उसके अंदर हर प्रोजेक्ट के लिए एक डायरेक्टरी बनाता हूँ।
<div class="termy">
```console
// होम डायरेक्टरी में जाएँ
$ cd
// अपने सभी कोड प्रोजेक्ट्स के लिए एक डायरेक्टरी बनाएँ
$ mkdir code
// उस कोड डायरेक्टरी में जाएँ
$ cd code
// इस प्रोजेक्ट के लिए एक डायरेक्टरी बनाएँ
$ mkdir awesome-project
// उस प्रोजेक्ट डायरेक्टरी में जाएँ
$ cd awesome-project
```
</div>
## वर्चुअल एनवायरनमेंट बनाएँ { #create-a-virtual-environment }
जब आप किसी Python प्रोजेक्ट पर **पहली बार** काम शुरू करते हैं, तो एक वर्चुअल एनवायरनमेंट **<dfn title="अन्य विकल्प भी हैं, यह एक सरल दिशानिर्देश है">अपने प्रोजेक्ट के अंदर</dfn>** बनाएँ।
/// tip | सुझाव
आपको यह **हर प्रोजेक्ट के लिए केवल एक बार** करना होता है, हर बार काम करते समय नहीं।
///
//// tab | `venv`
वर्चुअल एनवायरनमेंट बनाने के लिए, आप Python के साथ आने वाले `venv` module का उपयोग कर सकते हैं।
<div class="termy">
```console
$ python -m venv .venv
```
</div>
/// details | उस कमांड का क्या अर्थ है
* `python`: `python` नाम के प्रोग्राम का उपयोग करें
* `-m`: किसी module को script की तरह call करें, अगला हम उसे बताएँगे कि कौन-सा module
* `venv`: `venv` नाम के module का उपयोग करें जो सामान्यतः Python के साथ इंस्टॉल आता है
* `.venv`: नई डायरेक्टरी `.venv` में वर्चुअल एनवायरनमेंट बनाएँ
///
////
//// tab | `uv`
अगर आपके पास [`uv`](https://github.com/astral-sh/uv) इंस्टॉल है, तो आप इसका उपयोग वर्चुअल एनवायरनमेंट बनाने के लिए कर सकते हैं।
<div class="termy">
```console
$ uv venv
```
</div>
/// tip | सुझाव
डिफ़ॉल्ट रूप से, `uv` `.venv` नाम की डायरेक्टरी में वर्चुअल एनवायरनमेंट बनाएगा।
लेकिन आप डायरेक्टरी नाम के साथ एक अतिरिक्त argument देकर इसे customize कर सकते हैं।
///
////
वह कमांड `.venv` नाम की डायरेक्टरी में एक नया वर्चुअल एनवायरनमेंट बनाता है।
/// details | `.venv` या कोई दूसरा नाम
आप वर्चुअल एनवायरनमेंट को किसी दूसरी डायरेक्टरी में बना सकते हैं, लेकिन इसे `.venv` कहने की एक convention है।
///
## वर्चुअल एनवायरनमेंट सक्रिय करें { #activate-the-virtual-environment }
नए वर्चुअल एनवायरनमेंट को activate करें ताकि आप जो भी Python command चलाएँ या जो package install करें, वह इसका उपयोग करे।
/// tip | सुझाव
प्रोजेक्ट पर काम करने के लिए **हर बार** जब आप एक **नया terminal session** शुरू करें, तो यह करें।
///
//// tab | Linux, macOS
<div class="termy">
```console
$ source .venv/bin/activate
```
</div>
////
//// tab | Windows PowerShell
<div class="termy">
```console
$ .venv\Scripts\Activate.ps1
```
</div>
////
//// tab | Windows Bash
या अगर आप Windows के लिए Bash का उपयोग करते हैं (जैसे [Git Bash](https://gitforwindows.org/)):
<div class="termy">
```console
$ source .venv/Scripts/activate
```
</div>
////
/// tip | सुझाव
हर बार जब आप उस environment में कोई **नया package** install करें, तो environment को फिर से **activate** करें।
यह सुनिश्चित करता है कि अगर आप उस package द्वारा install किया गया कोई **terminal (<abbr title="command line interface - कमांड लाइन इंटरफ़ेस">CLI</abbr>) program** इस्तेमाल करते हैं, तो आप अपने वर्चुअल एनवायरनमेंट वाला ही उपयोग करें, कोई और नहीं जो global रूप से install हो सकता है, शायद आपकी ज़रूरत से अलग version के साथ।
///
## जाँचें कि वर्चुअल एनवायरनमेंट सक्रिय है { #check-the-virtual-environment-is-active }
जाँचें कि वर्चुअल एनवायरनमेंट active है (पिछली command ने काम किया)।
/// tip | सुझाव
यह **वैकल्पिक** है, लेकिन यह **जाँचने** का एक अच्छा तरीका है कि सब कुछ अपेक्षा के अनुसार काम कर रहा है और आप वही वर्चुअल एनवायरनमेंट इस्तेमाल कर रहे हैं जिसका आपने इरादा किया था।
///
//// tab | Linux, macOS, Windows Bash
<div class="termy">
```console
$ which python
/home/user/code/awesome-project/.venv/bin/python
```
</div>
अगर यह `.venv/bin/python` पर `python` binary दिखाता है, आपके प्रोजेक्ट के अंदर (इस मामले में `awesome-project`), तो यह काम कर गया। 🎉
////
//// tab | Windows PowerShell
<div class="termy">
```console
$ Get-Command python
C:\Users\user\code\awesome-project\.venv\Scripts\python
```
</div>
अगर यह `.venv\Scripts\python` पर `python` binary दिखाता है, आपके प्रोजेक्ट के अंदर (इस मामले में `awesome-project`), तो यह काम कर गया। 🎉
////
## `pip` अपग्रेड करें { #upgrade-pip }
/// tip | सुझाव
अगर आप [`uv`](https://github.com/astral-sh/uv) का उपयोग करते हैं, तो आप चीजें install करने के लिए `pip` की बजाय उसी का उपयोग करेंगे, इसलिए आपको `pip` upgrade करने की ज़रूरत नहीं है। 😎
///
अगर आप packages install करने के लिए `pip` का उपयोग कर रहे हैं (यह Python के साथ default रूप से आता है), तो आपको इसे latest version में **upgrade** करना चाहिए।
किसी package को install करते समय कई अजीब errors केवल पहले `pip` upgrade करने से हल हो जाते हैं।
/// tip | सुझाव
आप सामान्यतः यह **एक बार** करेंगे, वर्चुअल एनवायरनमेंट बनाने के ठीक बाद।
///
सुनिश्चित करें कि वर्चुअल एनवायरनमेंट active है (ऊपर वाली command से) और फिर चलाएँ:
<div class="termy">
```console
$ python -m pip install --upgrade pip
---> 100%
```
</div>
/// tip | सुझाव
कभी-कभी, pip upgrade करने की कोशिश करते समय आपको **`No module named pip`** error मिल सकता है।
अगर ऐसा होता है, तो नीचे दी गई command का उपयोग करके pip install और upgrade करें:
<div class="termy">
```console
$ python -m ensurepip --upgrade
---> 100%
```
</div>
यह command pip को install करेगी अगर वह पहले से install नहीं है और यह भी सुनिश्चित करेगी कि install किया गया pip का version कम से कम `ensurepip` में उपलब्ध version जितना नया हो।
///
## `.gitignore` जोड़ें { #add-gitignore }
अगर आप **Git** का उपयोग कर रहे हैं (आपको करना चाहिए), तो अपनी `.venv` की हर चीज़ को Git से exclude करने के लिए एक `.gitignore` file जोड़ें।
/// tip | सुझाव
अगर आपने वर्चुअल एनवायरनमेंट बनाने के लिए [`uv`](https://github.com/astral-sh/uv) का उपयोग किया है, तो यह आपके लिए पहले ही कर चुका है, आप यह step छोड़ सकते हैं। 😎
///
/// tip | सुझाव
यह **एक बार** करें, वर्चुअल एनवायरनमेंट बनाने के ठीक बाद।
///
<div class="termy">
```console
$ echo "*" > .venv/.gitignore
```
</div>
/// details | उस कमांड का क्या अर्थ है
* `echo "*"`: terminal में text `*` को "print" करेगा (अगला हिस्सा इसे थोड़ा बदल देता है)
* `>`: `>` के बाईं ओर वाली command द्वारा terminal में print की गई कोई भी चीज़ print नहीं होनी चाहिए, बल्कि `>` के दाईं ओर वाली file में लिखी जानी चाहिए
* `.gitignore`: उस file का नाम जहाँ text लिखा जाना चाहिए
और Git के लिए `*` का मतलब "सब कुछ" होता है। इसलिए, यह `.venv` directory में सब कुछ ignore करेगा।
वह command `.gitignore` file बनाएगी, इस content के साथ:
```gitignore
*
```
///
## Packages इंस्टॉल करें { #install-packages }
Environment activate करने के बाद, आप उसमें packages install कर सकते हैं।
/// tip | सुझाव
जब आप अपने प्रोजेक्ट के लिए आवश्यक packages install या upgrade कर रहे हों, तो यह **एक बार** करें।
अगर आपको किसी version को upgrade करना हो या कोई नया package जोड़ना हो, तो आप **यह फिर से करेंगे**
///
### सीधे Packages इंस्टॉल करें { #install-packages-directly }
अगर आप जल्दी में हैं और अपने प्रोजेक्ट की package requirements declare करने के लिए कोई file इस्तेमाल नहीं करना चाहते, तो आप उन्हें सीधे install कर सकते हैं।
/// tip | सुझाव
आपके program को जिन packages और versions की ज़रूरत है, उन्हें एक file में रखना (बहुत) अच्छा विचार है (उदाहरण के लिए `requirements.txt` या `pyproject.toml`)।
///
//// tab | `pip`
<div class="termy">
```console
$ pip install "fastapi[standard]"
---> 100%
```
</div>
////
//// tab | `uv`
अगर आपके पास [`uv`](https://github.com/astral-sh/uv) है:
<div class="termy">
```console
$ uv pip install "fastapi[standard]"
---> 100%
```
</div>
////
### `requirements.txt` से इंस्टॉल करें { #install-from-requirements-txt }
अगर आपके पास `requirements.txt` है, तो अब आप इसके packages install करने के लिए इसका उपयोग कर सकते हैं।
//// tab | `pip`
<div class="termy">
```console
$ pip install -r requirements.txt
---> 100%
```
</div>
////
//// tab | `uv`
अगर आपके पास [`uv`](https://github.com/astral-sh/uv) है:
<div class="termy">
```console
$ uv pip install -r requirements.txt
---> 100%
```
</div>
////
/// details | `requirements.txt`
कुछ packages वाला `requirements.txt` ऐसा दिख सकता है:
```requirements.txt
fastapi[standard]==0.113.0
pydantic==2.8.0
```
///
## अपना Program चलाएँ { #run-your-program }
वर्चुअल एनवायरनमेंट activate करने के बाद, आप अपना program चला सकते हैं, और यह आपके वर्चुअल एनवायरनमेंट के अंदर मौजूद Python का उपयोग करेगा, उन packages के साथ जिन्हें आपने वहाँ install किया है।
<div class="termy">
```console
$ python main.py
Hello World
```
</div>
## अपना Editor कॉन्फ़िगर करें { #configure-your-editor }
आप शायद एक editor का उपयोग करेंगे, सुनिश्चित करें कि आप इसे उसी वर्चुअल एनवायरनमेंट का उपयोग करने के लिए configure करें जिसे आपने बनाया है (यह शायद इसे autodetect कर लेगा), ताकि आपको autocompletion और inline errors मिल सकें।
उदाहरण के लिए:
* [VS Code](https://code.visualstudio.com/docs/python/environments#_select-and-activate-an-environment)
* [PyCharm](https://www.jetbrains.com/help/pycharm/creating-virtual-environment.html)
/// tip | सुझाव
आपको सामान्यतः यह केवल **एक बार** करना होता है, जब आप वर्चुअल एनवायरनमेंट बनाते हैं।
///
## वर्चुअल एनवायरनमेंट निष्क्रिय करें { #deactivate-the-virtual-environment }
जब आप अपने प्रोजेक्ट पर काम कर लें, तो आप वर्चुअल एनवायरनमेंट को **deactivate** कर सकते हैं।
<div class="termy">
```console
$ deactivate
```
</div>
इस तरह, जब आप `python` चलाएँगे, तो यह वहाँ install packages वाले उस वर्चुअल एनवायरनमेंट से इसे चलाने की कोशिश नहीं करेगा।
## काम करने के लिए तैयार { #ready-to-work }
अब आप अपने प्रोजेक्ट पर काम शुरू करने के लिए तैयार हैं।
/// tip | सुझाव
क्या आप समझना चाहते हैं कि ऊपर की सारी चीज़ें क्या हैं?
आगे पढ़ते रहें। 👇🤓
///
## वर्चुअल एनवायरनमेंट क्यों { #why-virtual-environments }
FastAPI के साथ काम करने के लिए आपको [Python](https://www.python.org/) install करना होगा।
उसके बाद, आपको FastAPI और कोई भी अन्य **packages** जिन्हें आप इस्तेमाल करना चाहते हैं, **install** करने होंगे।
Packages install करने के लिए आप सामान्यतः Python के साथ आने वाली `pip` command (या समान alternatives) का उपयोग करेंगे।
फिर भी, अगर आप सीधे `pip` का उपयोग करते हैं, तो packages आपके **global Python environment** (Python की global installation) में install हो जाएँगे।
### समस्या { #the-problem }
तो, global Python environment में packages install करने में समस्या क्या है?
किसी समय, आप शायद कई अलग-अलग programs लिखेंगे जो **अलग-अलग packages** पर निर्भर करते हैं। और जिन projects पर आप काम करेंगे उनमें से कुछ उसी package के **अलग-अलग versions** पर निर्भर होंगे। 😱
उदाहरण के लिए, आप `philosophers-stone` नाम का एक project बना सकते हैं, यह program **`harry`, version `1`** नाम के किसी दूसरे package पर निर्भर करता है। इसलिए, आपको `harry` install करना होगा।
```mermaid
flowchart LR
stone(philosophers-stone) -->|requires| harry-1[harry v1]
```
फिर, कुछ समय बाद, आप `prisoner-of-azkaban` नाम का दूसरा project बनाते हैं, और यह project भी `harry` पर निर्भर करता है, लेकिन इस project को **`harry` version `3`** चाहिए।
```mermaid
flowchart LR
azkaban(prisoner-of-azkaban) --> |requires| harry-3[harry v3]
```
लेकिन अब समस्या यह है कि अगर आप packages को local **वर्चुअल एनवायरनमेंट** में install करने के बजाय globally (global environment में) install करते हैं, तो आपको चुनना होगा कि `harry` का कौन-सा version install करना है।
अगर आप `philosophers-stone` चलाना चाहते हैं, तो आपको पहले `harry` version `1` install करना होगा, उदाहरण के लिए:
<div class="termy">
```console
$ pip install "harry==1"
```
</div>
और फिर आपके global Python environment में `harry` version `1` install हो जाएगा।
```mermaid
flowchart LR
subgraph global[global env]
harry-1[harry v1]
end
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) -->|requires| harry-1
end
```
लेकिन फिर अगर आप `prisoner-of-azkaban` चलाना चाहते हैं, तो आपको `harry` version `1` uninstall करके `harry` version `3` install करना होगा (या सिर्फ version `3` install करने से version `1` automatically uninstall हो जाएगा)।
<div class="termy">
```console
$ pip install "harry==3"
```
</div>
और फिर आपके global Python environment में `harry` version `3` install हो जाएगा।
और अगर आप `philosophers-stone` फिर से चलाने की कोशिश करते हैं, तो संभावना है कि यह **काम न करे** क्योंकि इसे `harry` version `1` चाहिए।
```mermaid
flowchart LR
subgraph global[global env]
harry-1[<strike>harry v1</strike>]
style harry-1 fill:#ccc,stroke-dasharray: 5 5
harry-3[harry v3]
end
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) -.-x|⛔️| harry-1
end
subgraph azkaban-project[prisoner-of-azkaban project]
azkaban(prisoner-of-azkaban) --> |requires| harry-3
end
```
/// tip | सुझाव
Python packages में **नए versions** में **breaking changes से बचने** की पूरी कोशिश करना बहुत आम है, लेकिन सुरक्षित रहना बेहतर है, और नए versions को जानबूझकर तथा तब install करना बेहतर है जब आप tests चलाकर जाँच सकें कि सब कुछ सही तरीके से काम कर रहा है।
///
अब, यही चीज़ उन **कई** अन्य **packages** के साथ कल्पना करें जिन पर आपके सभी **projects निर्भर करते हैं**। इसे manage करना बहुत कठिन है। और संभवतः आप कुछ projects को packages के कुछ **incompatible versions** के साथ चला देंगे, और यह नहीं जान पाएँगे कि कुछ काम क्यों नहीं कर रहा।
साथ ही, आपके operating system (जैसे Linux, Windows, macOS) के आधार पर, उसमें Python पहले से install आया हो सकता है। और उस मामले में संभवतः कुछ packages कुछ specific versions के साथ pre-installed होंगे जो **आपके system के लिए आवश्यक** हैं। अगर आप global Python environment में packages install करते हैं, तो आप अपने operating system के साथ आए कुछ programs को **break** कर सकते हैं।
## Packages कहाँ इंस्टॉल होते हैं { #where-are-packages-installed }
जब आप Python install करते हैं, तो यह आपके computer पर कुछ files वाली कुछ directories बनाता है।
इनमें से कुछ directories वे होती हैं जो आपके द्वारा install किए गए सभी packages को रखने की जिम्मेदार होती हैं।
जब आप चलाते हैं:
<div class="termy">
```console
// इसे अभी न चलाएँ, यह केवल एक उदाहरण है 🤓
$ pip install "fastapi[standard]"
---> 100%
```
</div>
तो यह FastAPI code वाली एक compressed file download करेगा, सामान्यतः [PyPI](https://pypi.org/project/fastapi/) से।
यह उन अन्य packages की files भी **download** करेगा जिन पर FastAPI निर्भर करता है।
फिर यह उन सभी files को **extract** करेगा और उन्हें आपके computer की एक directory में रखेगा।
Default रूप से, यह उन downloaded और extracted files को उस directory में रखेगा जो आपकी Python installation के साथ आती है, वही **global environment** है।
## वर्चुअल एनवायरनमेंट्स क्या हैं { #what-are-virtual-environments }
सभी packages को global environment में रखने की समस्याओं का समाधान है कि आप जिस भी project पर काम करते हैं उसके लिए **एक वर्चुअल एनवायरनमेंट** उपयोग करें।
एक वर्चुअल एनवायरनमेंट एक **directory** है, global वाली के बहुत समान, जहाँ आप किसी project के लिए packages install कर सकते हैं।
इस तरह, हर project का अपना वर्चुअल एनवायरनमेंट (`.venv` directory) होगा, अपने packages के साथ।
```mermaid
flowchart TB
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) --->|requires| harry-1
subgraph venv1[.venv]
harry-1[harry v1]
end
end
subgraph azkaban-project[prisoner-of-azkaban project]
azkaban(prisoner-of-azkaban) --->|requires| harry-3
subgraph venv2[.venv]
harry-3[harry v3]
end
end
stone-project ~~~ azkaban-project
```
## वर्चुअल एनवायरनमेंट activate करने का क्या मतलब है { #what-does-activating-a-virtual-environment-mean }
जब आप किसी वर्चुअल एनवायरनमेंट को activate करते हैं, उदाहरण के लिए:
//// tab | Linux, macOS
<div class="termy">
```console
$ source .venv/bin/activate
```
</div>
////
//// tab | Windows PowerShell
<div class="termy">
```console
$ .venv\Scripts\Activate.ps1
```
</div>
////
//// tab | Windows Bash
या अगर आप Windows के लिए Bash का उपयोग करते हैं (जैसे [Git Bash](https://gitforwindows.org/)):
<div class="termy">
```console
$ source .venv/Scripts/activate
```
</div>
////
वह command कुछ [environment variables](environment-variables.md) बनाएगी या modify करेगी, जो अगली commands के लिए उपलब्ध होंगे।
उन variables में से एक `PATH` variable है।
/// tip | सुझाव
आप [Environment Variables](environment-variables.md#path-environment-variable) section में `PATH` environment variable के बारे में और जान सकते हैं।
///
वर्चुअल एनवायरनमेंट activate करने से उसका path `.venv/bin` (Linux और macOS पर) या `.venv\Scripts` (Windows पर) `PATH` environment variable में जुड़ जाता है।
मान लीजिए कि environment activate करने से पहले, `PATH` variable ऐसा दिखता था:
//// tab | Linux, macOS
```plaintext
/usr/bin:/bin:/usr/sbin:/sbin
```
इसका मतलब है कि system programs को इनमें खोजता:
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
//// tab | Windows
```plaintext
C:\Windows\System32
```
इसका मतलब है कि system programs को इसमें खोजता:
* `C:\Windows\System32`
////
वर्चुअल एनवायरनमेंट activate करने के बाद, `PATH` variable कुछ ऐसा दिखेगा:
//// tab | Linux, macOS
```plaintext
/home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
इसका मतलब है कि system अब सबसे पहले programs को यहाँ खोजना शुरू करेगा:
```plaintext
/home/user/code/awesome-project/.venv/bin
```
बाकी directories में देखने से पहले।
तो, जब आप terminal में `python` type करते हैं, तो system Python program को यहाँ पाएगा
```plaintext
/home/user/code/awesome-project/.venv/bin/python
```
और उसी का उपयोग करेगा।
////
//// tab | Windows
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32
```
इसका मतलब है कि system अब सबसे पहले programs को यहाँ खोजना शुरू करेगा:
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts
```
बाकी directories में देखने से पहले।
तो, जब आप terminal में `python` type करते हैं, तो system Python program को यहाँ पाएगा
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts\python
```
और उसी का उपयोग करेगा।
////
एक महत्वपूर्ण detail यह है कि यह वर्चुअल एनवायरनमेंट path को `PATH` variable की **शुरुआत** में रखेगा। System इसे किसी भी अन्य उपलब्ध Python से **पहले** पाएगा। इस तरह, जब आप `python` चलाते हैं, तो यह किसी अन्य `python` (उदाहरण के लिए, global environment वाला `python`) के बजाय **वर्चुअल एनवायरनमेंट से** Python का उपयोग करेगा।
वर्चुअल एनवायरनमेंट activate करने से कुछ और चीजें भी बदलती हैं, लेकिन यह उसके द्वारा की जाने वाली सबसे महत्वपूर्ण चीज़ों में से एक है।
## वर्चुअल एनवायरनमेंट की जाँच करना { #checking-a-virtual-environment }
जब आप जाँचते हैं कि वर्चुअल एनवायरनमेंट active है या नहीं, उदाहरण के लिए:
//// tab | Linux, macOS, Windows Bash
<div class="termy">
```console
$ which python
/home/user/code/awesome-project/.venv/bin/python
```
</div>
////
//// tab | Windows PowerShell
<div class="termy">
```console
$ Get-Command python
C:\Users\user\code\awesome-project\.venv\Scripts\python
```
</div>
////
इसका मतलब है कि जो `python` program उपयोग किया जाएगा, वह **वर्चुअल एनवायरनमेंट में** मौजूद है।
आप Linux और macOS में `which` और Windows PowerShell में `Get-Command` का उपयोग करते हैं।
वह command जिस तरह काम करती है, वह यह है कि यह `PATH` environment variable में जाकर **हर path को क्रम से** check करेगी, `python` नाम के program को खोजते हुए। एक बार जब यह उसे ढूँढ लेती है, तो यह आपको उस program का **path दिखाएगी**।
सबसे महत्वपूर्ण हिस्सा यह है कि जब आप `python` call करते हैं, तो वही exact "`python`" execute होगा।
तो, आप confirm कर सकते हैं कि आप सही वर्चुअल एनवायरनमेंट में हैं या नहीं।
/// tip | सुझाव
एक वर्चुअल एनवायरनमेंट activate करना, एक Python पाना, और फिर **दूसरे project में चले जाना** आसान है।
और दूसरा project **काम नहीं करेगा** क्योंकि आप **गलत Python** का उपयोग कर रहे हैं, जो किसी दूसरे project के वर्चुअल एनवायरनमेंट से है।
यह check कर पाना उपयोगी है कि कौन-सा `python` उपयोग हो रहा है। 🤓
///
## वर्चुअल एनवायरनमेंट deactivate क्यों करें { #why-deactivate-a-virtual-environment }
उदाहरण के लिए, आप `philosophers-stone` project पर काम कर रहे हो सकते हैं, **उस वर्चुअल एनवायरनमेंट को activate** करके, packages install करके और उस environment के साथ काम करके।
और फिर आप **किसी दूसरे project** `prisoner-of-azkaban` पर काम करना चाहते हैं।
आप उस project में जाते हैं:
<div class="termy">
```console
$ cd ~/code/prisoner-of-azkaban
```
</div>
अगर आप `philosophers-stone` के लिए वर्चुअल एनवायरनमेंट को deactivate नहीं करते, तो जब आप terminal में `python` चलाएँगे, यह `philosophers-stone` से Python का उपयोग करने की कोशिश करेगा।
<div class="termy">
```console
$ cd ~/code/prisoner-of-azkaban
$ python main.py
// sirius import करने में error, यह install नहीं है 😱
Traceback (most recent call last):
File "main.py", line 1, in <module>
import sirius
```
</div>
लेकिन अगर आप वर्चुअल एनवायरनमेंट deactivate करके `prisoner-of-azkaban` के लिए नया वाला activate करते हैं, तो जब आप `python` चलाएँगे, यह `prisoner-of-azkaban` में मौजूद वर्चुअल एनवायरनमेंट से Python का उपयोग करेगा।
<div class="termy">
```console
$ cd ~/code/prisoner-of-azkaban
// deactivate करने के लिए आपको पुरानी directory में होने की ज़रूरत नहीं है, आप जहाँ भी हों वहाँ से कर सकते हैं, दूसरे project में जाने के बाद भी 😎
$ deactivate
// prisoner-of-azkaban/.venv में वर्चुअल एनवायरनमेंट activate करें 🚀
$ source .venv/bin/activate
// अब जब आप python चलाएँगे, तो यह इस वर्चुअल एनवायरनमेंट में install package sirius को पाएगा ✨
$ python main.py
I solemnly swear 🐺
```
</div>
## Alternatives { #alternatives }
यह आपको शुरू करने और यह सिखाने के लिए एक सरल guide है कि सब कुछ **अंदर से** कैसे काम करता है।
Virtual environments, package dependencies (requirements), projects को manage करने के कई **alternatives** हैं।
जब आप तैयार हों और **पूरे project को manage** करने के लिए कोई tool उपयोग करना चाहें, package dependencies, virtual environments आदि सहित, तो मैं सुझाव दूँगा कि आप [uv](https://github.com/astral-sh/uv) आज़माएँ।
`uv` बहुत सारी चीज़ें कर सकता है, यह कर सकता है:
* आपके लिए **Python install** करना, अलग-अलग versions सहित
* आपके projects के लिए **वर्चुअल एनवायरनमेंट** manage करना
* **Packages** install करना
* आपके project के लिए package **dependencies और versions** manage करना
* सुनिश्चित करना कि आपके पास install करने के लिए packages और versions का **exact** set हो, उनकी dependencies सहित, ताकि आप सुनिश्चित हो सकें कि आप अपने project को production में ठीक उसी तरह चला सकते हैं जैसे development के दौरान अपने computer पर चलाते हैं, इसे **locking** कहा जाता है
* और कई अन्य चीज़ें
## निष्कर्ष { #conclusion }
अगर आपने यह सब पढ़ा और समझा है, तो अब **आप वर्चुअल एनवायरनमेंट्स के बारे में** वहाँ मौजूद कई developers से कहीं ज़्यादा जानते हैं। 🤓
इन details को जानना भविष्य में उस समय बहुत संभवतः उपयोगी होगा जब आप किसी ऐसी चीज़ को debug कर रहे होंगे जो complex लगती है, लेकिन आपको पता होगा कि **यह सब अंदर से कैसे काम करता है**। 😎

View File

View File

@@ -0,0 +1,5 @@
from fastapi import FastAPI
app = FastAPI()
app.frontend("/", directory="dist")

View File

@@ -0,0 +1,5 @@
from fastapi import FastAPI
app = FastAPI()
app.frontend("/", directory="dist", fallback="index.html")

View File

@@ -0,0 +1,5 @@
from fastapi import FastAPI
app = FastAPI()
app.frontend("/", directory="dist", fallback="404.html")

View File

@@ -0,0 +1,7 @@
from fastapi import APIRouter, FastAPI
app = FastAPI()
router = APIRouter()
router.frontend("/", directory="dist", fallback="index.html")
app.include_router(router, prefix="/app")

View File

@@ -0,0 +1,5 @@
from fastapi import FastAPI
app = FastAPI()
app.frontend("/", directory="dist", fallback=None)

View File

@@ -0,0 +1,5 @@
from fastapi import FastAPI
app = FastAPI()
app.frontend("/", directory="dist", check_dir=False)

View File

@@ -288,6 +288,32 @@ There could be exceptions, but try to follow this convention.
Apply shared dependencies at the router level via `dependencies=[Depends(...)]`.
## Serve Frontend Apps
Use `app.frontend()` to serve a built static frontend app, for example a directory generated by Vite, Astro, Angular, Svelte, Vue, or a similar tool.
```python
from fastapi import FastAPI
app = FastAPI()
app.frontend("/", directory="dist")
```
Use `router.frontend()` when the frontend belongs to an `APIRouter`; normal router prefix behavior applies when the router is included.
```python
from fastapi import APIRouter, FastAPI
app = FastAPI()
router = APIRouter(prefix="/admin")
router.frontend("/", directory="admin-dist")
app.include_router(router)
```
`app.frontend()` and `router.frontend()` are low-priority routes: regular API routes are matched first, then frontend files and client-side routing fallbacks. Use this for single-page apps and built frontend assets instead of mounting `StaticFiles` manually.
## Dependency Injection
See [the dependency injection reference](references/dependencies.md) for detailed patterns including `yield` with `scope`, and class dependencies.

View File

@@ -1,6 +1,6 @@
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
__version__ = "0.137.2"
__version__ = "0.138.0"
from starlette import status as status

View File

@@ -1,6 +1,7 @@
import os
from collections.abc import Awaitable, Callable, Coroutine, Sequence
from enum import Enum
from typing import Annotated, Any, TypeVar
from typing import Annotated, Any, Literal, TypeVar
from annotated_doc import Doc
from fastapi import routing
@@ -1218,6 +1219,79 @@ class FastAPI(Starlette):
generate_unique_id_function=generate_unique_id_function,
)
def frontend(
self,
path: Annotated[
str,
Doc(
"""
The URL path prefix where the frontend build should be served.
"""
),
],
*,
directory: Annotated[
str | os.PathLike[str],
Doc(
"""
The directory containing the static frontend build output.
"""
),
],
fallback: Annotated[
Literal["auto", "index.html", "404.html"] | None,
Doc(
"""
The fallback file behavior for missing frontend paths.
"""
),
] = "auto",
check_dir: Annotated[
bool,
Doc(
"""
Check that the frontend directory exists when the app is created.
"""
),
] = True,
) -> None:
"""
Serve a static frontend build as low-priority routes.
Use this for frontend tools that build static files into a directory,
such as `dist`. **FastAPI** path operations are checked first, and
the frontend files are checked only if no normal route matched.
A typical project could look like this:
```text
.
├── pyproject.toml
├── app
│ ├── __init__.py
│ └── main.py
└── dist
├── index.html
└── assets
└── app.js
```
Then in `app/main.py`:
```python
from fastapi import FastAPI
app = FastAPI()
app.frontend("/", directory="dist")
```
"""
self.router.frontend(
path,
directory=directory,
fallback=fallback,
check_dir=check_dir,
)
def api_route(
self,
path: str,

View File

@@ -1,9 +1,12 @@
import contextlib
import copy
import email.message
import errno
import functools
import inspect
import json
import os
import stat
import types
from collections.abc import (
AsyncIterator,
@@ -28,6 +31,7 @@ from enum import Enum, IntEnum
from typing import (
Annotated,
Any,
Literal,
Protocol,
TypeVar,
cast,
@@ -80,22 +84,25 @@ from starlette import routing
from starlette._exception_handler import wrap_app_handling_exceptions
from starlette._utils import get_route_path, is_async_callable
from starlette.concurrency import iterate_in_threadpool, run_in_threadpool
from starlette.datastructures import FormData, URLPath
from starlette.datastructures import URL, FormData, URLPath
from starlette.exceptions import HTTPException
from starlette.requests import Request
from starlette.responses import (
JSONResponse,
PlainTextResponse,
RedirectResponse,
Response,
StreamingResponse,
)
from starlette.routing import (
BaseRoute,
Match,
NoMatchFound,
compile_path,
get_name,
)
from starlette.routing import Mount as Mount # noqa
from starlette.staticfiles import StaticFiles
from starlette.types import AppType, ASGIApp, Lifespan, Receive, Scope, Send
from starlette.websockets import WebSocket
from typing_extensions import deprecated
@@ -819,6 +826,7 @@ class APIWebSocketRoute(routing.WebSocketRoute):
_FASTAPI_SCOPE_KEY = "fastapi"
_FASTAPI_EFFECTIVE_ROUTE_CONTEXT_KEY = "effective_route_context"
_FASTAPI_FRONTEND_PATH_KEY = "frontend_path"
_FASTAPI_INCLUDED_ROUTER_KEY = "included_router"
_effective_route_context_var: ContextVar[Any | None] = ContextVar(
"fastapi_effective_route_context", default=None
@@ -826,12 +834,25 @@ _effective_route_context_var: ContextVar[Any | None] = ContextVar(
_SCOPE_MISSING = object()
class _RouteWithPath(Protocol):
path: str
def _get_fastapi_scope(scope: Scope) -> dict[str, Any]:
fastapi_scope = scope.setdefault(_FASTAPI_SCOPE_KEY, {})
assert isinstance(fastapi_scope, dict)
return fastapi_scope
def _update_scope(scope: Scope, child_scope: Scope) -> None:
fastapi_child_scope = child_scope.get(_FASTAPI_SCOPE_KEY)
for key, value in child_scope.items():
if key != _FASTAPI_SCOPE_KEY:
scope[key] = value
if isinstance(fastapi_child_scope, dict):
_get_fastapi_scope(scope).update(fastapi_child_scope)
def _get_scope_effective_route_context(scope: Scope) -> Any | None:
return scope.get(_FASTAPI_SCOPE_KEY, {}).get(_FASTAPI_EFFECTIVE_ROUTE_CONTEXT_KEY)
@@ -1305,9 +1326,7 @@ class _RouterIncludeContext:
dependency_overrides_provider=self.dependency_overrides_provider,
)
def path_for(
self, route: APIRoute | routing.Route | routing.WebSocketRoute | routing.Mount
) -> str:
def path_for(self, route: _RouteWithPath) -> str:
return self.prefix + route.path
@@ -1503,6 +1522,10 @@ class _IncludedRouter(BaseRoute):
default_factory=list
)
_effective_candidates_version: int | None = None
_effective_low_priority_routes: list["_EffectiveRouteContext"] = field(
default_factory=list
)
_effective_low_priority_routes_version: int | None = None
def effective_candidates(self) -> list["_EffectiveRouteContext | _IncludedRouter"]:
routes_version = self.original_router._get_routes_version()
@@ -1525,6 +1548,28 @@ class _IncludedRouter(BaseRoute):
self._effective_candidates_version = routes_version
return self._effective_candidates
def effective_low_priority_routes(self) -> list["_EffectiveRouteContext"]:
routes_version = self.original_router._get_routes_version()
if routes_version == self._effective_low_priority_routes_version:
return self._effective_low_priority_routes
self._effective_low_priority_routes = []
for route in self.original_router._low_priority_routes:
route_context = self._build_effective_context(route)
if route_context is not None:
self._effective_low_priority_routes.append(route_context)
for route in self.original_router.routes:
if isinstance(route, _IncludedRouter):
child_context = self.include_context.combine(route.include_context)
child_branch = _IncludedRouter(
original_router=route.original_router,
include_context=child_context,
)
self._effective_low_priority_routes.extend(
child_branch.effective_low_priority_routes()
)
self._effective_low_priority_routes_version = routes_version
return self._effective_low_priority_routes
def _build_effective_context(
self, route: BaseRoute
) -> _EffectiveRouteContext | None:
@@ -1533,6 +1578,11 @@ class _IncludedRouter(BaseRoute):
original_route=route,
include_context=self.include_context,
)
if isinstance(route, _FrontendRouteGroup):
return _EffectiveRouteContext(
original_route=route,
starlette_route=route.with_prefix(self.include_context.prefix),
)
if isinstance(route, routing.Route):
starlette_route: BaseRoute = routing.Route(
self.include_context.path_for(route),
@@ -1720,6 +1770,294 @@ def _iter_routes_with_context(
yield route, None
def _normalize_frontend_path(path: str) -> str:
if not path:
raise AssertionError("A frontend path cannot be empty")
if not path.startswith("/"):
raise AssertionError("A frontend path must start with '/'")
if path != "/":
path = path.rstrip("/")
return path
def _join_frontend_paths(prefix: str, path: str) -> str:
if not prefix:
return path
if path == "/":
return prefix
return prefix + path
def _frontend_path_specificity(path: str) -> int:
if path == "/":
return 0
return len(path)
def _get_resolved_absolute_path(path: str | os.PathLike[str]) -> str:
return os.path.realpath(os.fspath(path))
class _FrontendStaticFiles(StaticFiles):
def __init__(
self,
*,
directory: str | os.PathLike[str],
fallback: Literal["auto", "index.html", "404.html"] | None,
check_dir: bool = True,
) -> None:
self.fallback = fallback
if check_dir and not os.path.isdir(directory):
raise RuntimeError(
f"Frontend directory '{directory}' does not exist. "
f"Resolved absolute path: '{_get_resolved_absolute_path(directory)}'"
)
super().__init__(
directory=directory,
html=True,
check_dir=check_dir,
follow_symlink=False,
)
if check_dir and fallback in {"index.html", "404.html"}:
self._check_fallback_file(fallback)
def _check_fallback_file(self, fallback: str) -> None:
_, stat_result = self.lookup_path(fallback)
if stat_result is None or not stat.S_ISREG(stat_result.st_mode):
raise RuntimeError(
f"Frontend fallback file '{fallback}' does not exist in "
f"directory '{self.directory}'. Resolved absolute directory: "
f"'{self._get_resolved_directory()}'"
)
def _get_resolved_directory(self) -> str:
assert self.directory is not None
return _get_resolved_absolute_path(self.directory)
def get_path(self, scope: Scope) -> str:
path = _get_fastapi_scope(scope).get(_FASTAPI_FRONTEND_PATH_KEY, "")
assert isinstance(path, str)
return os.path.normpath(os.path.join(*path.split("/")))
async def get_response(self, path: str, scope: Scope) -> Response:
if scope["method"] not in ("GET", "HEAD"):
raise HTTPException(status_code=405)
try:
full_path, stat_result = await run_in_threadpool(self.lookup_path, path)
except PermissionError:
raise HTTPException(status_code=401) from None
except OSError as exc:
if exc.errno == errno.ENAMETOOLONG:
raise HTTPException(status_code=404) from None
raise exc
except ValueError:
raise HTTPException(status_code=404) from None
if stat_result and stat.S_ISREG(stat_result.st_mode):
return self.file_response(full_path, stat_result, scope)
if stat_result and stat.S_ISDIR(stat_result.st_mode):
index_path = os.path.join(path, "index.html")
full_path, stat_result = await run_in_threadpool(
self.lookup_path, index_path
)
if stat_result is not None and stat.S_ISREG(stat_result.st_mode):
if not scope["path"].endswith("/"):
url = URL(scope=scope)
url = url.replace(path=url.path + "/")
return RedirectResponse(url=url)
return self.file_response(full_path, stat_result, scope)
if self.fallback == "404.html" or (
self.fallback == "auto" and self._fallback_file_exists("404.html")
):
return await self._fallback_response("404.html", scope, status_code=404)
if (
self.fallback == "index.html"
or (self.fallback == "auto" and self._fallback_file_exists("index.html"))
) and _is_frontend_navigation_request(scope):
return await self._fallback_response("index.html", scope, status_code=200)
raise HTTPException(status_code=404)
def _fallback_file_exists(self, fallback: str) -> bool:
_, stat_result = self.lookup_path(fallback)
return stat_result is not None and stat.S_ISREG(stat_result.st_mode)
async def _fallback_response(
self, fallback: str, scope: Scope, *, status_code: int
) -> Response:
full_path, stat_result = await run_in_threadpool(self.lookup_path, fallback)
if stat_result is None or not stat.S_ISREG(stat_result.st_mode):
raise RuntimeError(
f"Frontend fallback file '{fallback}' does not exist in "
f"directory '{self.directory}'. Resolved absolute directory: "
f"'{self._get_resolved_directory()}'"
)
return self.file_response(
full_path, stat_result, scope, status_code=status_code
)
def _iter_accept_media_types(accept: str) -> Iterator[tuple[str, float]]:
for raw_value in accept.split(","):
message = email.message.Message()
message["content-type"] = raw_value.strip()
q = message.get_param("q")
quality = 1.0
if isinstance(q, str):
try:
quality = float(q)
except ValueError:
pass
yield (
f"{message.get_content_maintype()}/{message.get_content_subtype()}",
quality,
)
def _is_frontend_navigation_request(scope: Scope) -> bool:
route_path = get_route_path(scope)
final_segment = route_path.rsplit("/", 1)[-1]
if os.path.splitext(final_segment)[1]:
return False
request = Request(scope)
wildcard_accepted = False
html_rejected = False
for media_type, quality in _iter_accept_media_types(
request.headers.get("accept", "")
):
if media_type in {"text/html", "application/xhtml+xml"}:
if quality == 0:
html_rejected = True
else:
return True
elif media_type == "*/*" and quality != 0:
wildcard_accepted = True
return wildcard_accepted and not html_rejected
class _FrontendRoute(BaseRoute):
def __init__(
self,
path: str,
*,
directory: str | os.PathLike[str],
fallback: Literal["auto", "index.html", "404.html"] | None = "auto",
check_dir: bool = True,
) -> None:
if fallback not in {"auto", "index.html", "404.html", None}:
raise AssertionError(
"fallback must be 'auto', 'index.html', '404.html', or None"
)
self.path = _normalize_frontend_path(path)
self.methods = {"GET", "HEAD"}
self.app = _FrontendStaticFiles(
directory=directory, fallback=fallback, check_dir=check_dir
)
def with_path(self, path: str) -> "_FrontendRoute":
route = copy.copy(self)
route.path = _normalize_frontend_path(path)
return route
def matches(self, scope: Scope) -> tuple[Match, Scope]:
if scope["type"] != "http":
return Match.NONE, {}
frontend_path = self._get_frontend_path(get_route_path(scope))
if frontend_path is None:
return Match.NONE, {}
child_scope = {_FASTAPI_SCOPE_KEY: {_FASTAPI_FRONTEND_PATH_KEY: frontend_path}}
if scope["method"] not in self.methods:
return Match.PARTIAL, child_scope
return Match.FULL, child_scope
def _get_frontend_path(self, route_path: str) -> str | None:
if self.path == "/":
return route_path.lstrip("/")
if route_path == self.path:
return ""
prefix = self.path + "/"
if route_path.startswith(prefix):
return route_path[len(prefix) :]
return None
async def handle(self, scope: Scope, receive: Receive, send: Send) -> None:
await self.app(scope, receive, send)
def url_path_for(self, name: str, /, **path_params: Any) -> URLPath:
raise NoMatchFound(name, path_params)
class _FrontendRouteGroup(BaseRoute):
def __init__(self) -> None:
self.routes: list[_FrontendRoute] = []
def add_frontend_route(
self,
path: str,
*,
directory: str | os.PathLike[str],
fallback: Literal["auto", "index.html", "404.html"] | None = "auto",
check_dir: bool = True,
) -> None:
self.routes.append(
_FrontendRoute(
path,
directory=directory,
fallback=fallback,
check_dir=check_dir,
)
)
def with_prefix(self, prefix: str) -> "_FrontendRouteGroup":
route_group = copy.copy(self)
route_group.routes = [
route.with_path(_join_frontend_paths(prefix, route.path))
for route in self.routes
]
return route_group
def matches(self, scope: Scope) -> tuple[Match, Scope]:
match, child_scope, _ = self._match(scope)
return match, child_scope
def _match(self, scope: Scope) -> tuple[Match, Scope, _FrontendRoute | None]:
full: tuple[Scope, _FrontendRoute] | None = None
partial: tuple[Scope, _FrontendRoute] | None = None
for route in self.routes:
match, child_scope = route.matches(scope)
if match == Match.FULL:
if full is None or _frontend_path_specificity(
route.path
) > _frontend_path_specificity(full[1].path):
full = (child_scope, route)
elif match == Match.PARTIAL:
if partial is None or _frontend_path_specificity(
route.path
) > _frontend_path_specificity(partial[1].path):
partial = (child_scope, route)
if full is not None:
child_scope, route = full
return Match.FULL, child_scope, route
if partial is not None:
child_scope, route = partial
return Match.PARTIAL, child_scope, route
return Match.NONE, {}, None
async def handle(self, scope: Scope, receive: Receive, send: Send) -> None:
match, child_scope, route = self._match(scope)
if match == Match.NONE or route is None:
raise HTTPException(status_code=404)
_update_scope(scope, child_scope)
await route.handle(scope, receive, send)
def url_path_for(self, name: str, /, **path_params: Any) -> URLPath:
raise NoMatchFound(name, path_params)
class APIRouter(routing.Router):
"""
`APIRouter` class, used to group *path operations*, for example to structure
@@ -2032,6 +2370,8 @@ class APIRouter(routing.Router):
self.generate_unique_id_function = generate_unique_id_function
self.strict_content_type = strict_content_type
self._routes_version = 0
self._low_priority_routes: list[BaseRoute] = []
self._frontend_routes: _FrontendRouteGroup | None = None
def _mark_routes_changed(self) -> None:
self._routes_version += 1
@@ -2093,6 +2433,150 @@ class APIRouter(routing.Router):
super().add_websocket_route(path, endpoint, name=name)
self._mark_routes_changed()
def frontend(
self,
path: Annotated[
str,
Doc(
"""
The URL path prefix where the frontend build should be served.
"""
),
],
*,
directory: Annotated[
str | os.PathLike[str],
Doc(
"""
The directory containing the static frontend build output.
"""
),
],
fallback: Annotated[
Literal["auto", "index.html", "404.html"] | None,
Doc(
"""
The fallback file behavior for missing frontend paths.
"""
),
] = "auto",
check_dir: Annotated[
bool,
Doc(
"""
Check that the frontend directory exists when the app is created.
"""
),
] = True,
) -> None:
"""
Serve a static frontend build as low-priority routes.
Use this for frontend tools that build static files into a directory,
such as `dist`. **FastAPI** path operations are checked first, and
the frontend files are checked only if no normal route matched.
A typical project could look like this:
```text
.
├── pyproject.toml
├── app
│ ├── __init__.py
│ └── main.py
└── dist
├── index.html
└── assets
└── app.js
```
Then in `app/main.py`:
```python
from fastapi import APIRouter, FastAPI
app = FastAPI()
router = APIRouter()
router.frontend("/", directory="dist")
app.include_router(router)
```
"""
normalized_path = _normalize_frontend_path(path)
if self._frontend_routes is None:
self._frontend_routes = _FrontendRouteGroup()
self._low_priority_routes.append(self._frontend_routes)
self._frontend_routes.add_frontend_route(
_join_frontend_paths(self.prefix, normalized_path),
directory=directory,
fallback=fallback,
check_dir=check_dir,
)
self._mark_routes_changed()
async def app(self, scope: Scope, receive: Receive, send: Send) -> None:
assert scope["type"] in ("http", "websocket", "lifespan")
if "router" not in scope:
scope["router"] = self
if scope["type"] == "lifespan":
await self.lifespan(scope, receive, send)
return
partial: tuple[BaseRoute, Scope] | None = None
for route in self.routes:
match, child_scope = route.matches(scope)
if match == Match.FULL:
scope.update(child_scope)
await route.handle(scope, receive, send)
return
if match == Match.PARTIAL and partial is None:
partial = (route, child_scope)
if partial is not None:
route, child_scope = partial
scope.update(child_scope)
await route.handle(scope, receive, send)
return
route_path = get_route_path(scope)
if scope["type"] == "http" and self.redirect_slashes and route_path != "/":
redirect_scope = dict(scope)
if route_path.endswith("/"):
redirect_scope["path"] = redirect_scope["path"].rstrip("/")
else:
redirect_scope["path"] = redirect_scope["path"] + "/"
for route in self.routes:
match, _ = route.matches(redirect_scope)
if match != Match.NONE:
redirect_url = URL(scope=redirect_scope)
response = RedirectResponse(url=str(redirect_url))
await response(scope, receive, send)
return
(
low_priority_match,
low_priority_scope,
low_priority_route,
low_priority_context,
) = self._match_low_priority(scope)
if low_priority_match != Match.NONE and low_priority_route is not None:
_update_scope(scope, low_priority_scope)
if low_priority_context is not None:
_get_fastapi_scope(scope)[_FASTAPI_EFFECTIVE_ROUTE_CONTEXT_KEY] = (
low_priority_context
)
original_route = low_priority_context.original_route
if isinstance(original_route, APIRoute):
scope["route"] = original_route
await original_route.handle(scope, receive, send)
return
await low_priority_route.handle(scope, receive, send)
return
await self.default(scope, receive, send)
async def handle(self, scope: Scope, receive: Receive, send: Send) -> None:
included_router = _get_scope_included_router(scope)
if (
@@ -2113,6 +2597,60 @@ class APIRouter(routing.Router):
return match, child_scope
return Match.NONE, {}
def _iter_low_priority_routes(
self,
) -> Iterator[BaseRoute | _EffectiveRouteContext]:
yield from self._low_priority_routes
for route in self.routes:
if isinstance(route, _IncludedRouter):
yield from route.effective_low_priority_routes()
def _match_low_priority(
self, scope: Scope
) -> tuple[Match, Scope, BaseRoute | None, _EffectiveRouteContext | None]:
full: tuple[Scope, BaseRoute, _EffectiveRouteContext | None] | None = None
partial: tuple[Scope, BaseRoute, _EffectiveRouteContext | None] | None = None
for candidate in self._iter_low_priority_routes():
route: BaseRoute
if isinstance(candidate, _EffectiveRouteContext):
route_context: _EffectiveRouteContext | None = candidate
original_route = candidate.original_route
if isinstance(original_route, APIRoute):
fastapi_scope = _get_fastapi_scope(scope)
previous_context = fastapi_scope.get(
_FASTAPI_EFFECTIVE_ROUTE_CONTEXT_KEY, _SCOPE_MISSING
)
fastapi_scope[_FASTAPI_EFFECTIVE_ROUTE_CONTEXT_KEY] = route_context
try:
match, child_scope = original_route.matches(scope)
finally:
_restore_fastapi_scope_key(
scope,
_FASTAPI_EFFECTIVE_ROUTE_CONTEXT_KEY,
previous_context,
)
route = original_route
else:
match, child_scope = candidate.matches(scope)
route = candidate.starlette_route or original_route
else:
route_context = None
match, child_scope = candidate.matches(scope)
route = candidate
if match == Match.FULL:
if full is None:
full = (child_scope, route, route_context)
elif match == Match.PARTIAL:
if partial is None:
partial = (child_scope, route, route_context)
if full is not None:
child_scope, route, route_context = full
return Match.FULL, child_scope, route, route_context
if partial is not None:
child_scope, route, route_context = partial
return Match.PARTIAL, child_scope, route, route_context
return Match.NONE, {}, None, None
def route(
self,
path: str,

View File

@@ -319,7 +319,6 @@ extend-exclude = [
"docs/de/",
"docs/en/data/",
"docs/en/docs/img/",
"docs/en/docs/release-notes.md",
"docs/es/",
"docs/fr/",
"docs/ja/",
@@ -340,6 +339,16 @@ extend-exclude = [
"uv.lock",
]
[tool.typos.default]
extend-ignore-re = [
# GitHub usernames in @mentions
"@[a-zA-Z0-9](?:-?[a-zA-Z0-9])*",
# Quoted typo documented in a release note
"'wll' to 'will'",
# German article title in a release note
"FastAPI Modul.",
]
[tool.typos.default.extend-identifiers]
alls = "alls"

858
tests/test_frontend.py Normal file
View File

@@ -0,0 +1,858 @@
import errno
import os
import runpy
from pathlib import Path
import anyio
import pytest
from fastapi import APIRouter, FastAPI, HTTPException, Request, WebSocket
from fastapi.testclient import TestClient
from starlette.exceptions import HTTPException as StarletteHTTPException
from starlette.responses import PlainTextResponse, Response
from starlette.routing import BaseRoute, Match, NoMatchFound, Route
def write_file(path: Path, content: str) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(content)
def test_frontend_exact_prefix_path_serves_index(tmp_path: Path):
dist = tmp_path / "dist"
write_file(dist / "index.html", "app")
app = FastAPI()
app.frontend("/app", directory=dist)
response = TestClient(app).get("/app")
assert response.status_code == 200
assert response.text == "app"
def test_apirouter_frontend_with_router_prefix_and_frontend_subpath(tmp_path: Path):
dist = tmp_path / "dist"
write_file(dist / "asset.txt", "asset")
router = APIRouter(prefix="/internal")
router.frontend("/ui", directory=dist)
app = FastAPI()
app.include_router(router, prefix="/prefix")
response = TestClient(app).get("/prefix/internal/ui/asset.txt")
assert response.status_code == 200
assert response.text == "asset"
def test_frontend_fallback_rejects_invalid_fallback(tmp_path: Path):
dist = tmp_path / "dist"
dist.mkdir()
app = FastAPI()
with pytest.raises(AssertionError, match="fallback"):
app.frontend("/", directory=dist, fallback="invalid") # type: ignore[arg-type] # ty: ignore[invalid-argument-type]
def test_index_fallback_ignores_invalid_q_value(tmp_path: Path):
dist = tmp_path / "dist"
write_file(dist / "index.html", "app shell")
app = FastAPI()
app.frontend("/", directory=dist, fallback="index.html")
response = TestClient(app).get(
"/dashboard/settings", headers={"accept": "text/html; q=wat"}
)
assert response.status_code == 200
assert response.text == "app shell"
def test_frontend_static_files_lookup_errors(monkeypatch, tmp_path: Path):
dist = tmp_path / "dist"
write_file(dist / "index.html", "app")
app = FastAPI()
app.frontend("/", directory=dist)
frontend_routes = app.router._frontend_routes
assert frontend_routes is not None
static_files = frontend_routes.routes[0].app
def raise_permission_error(path: str):
raise PermissionError
monkeypatch.setattr(static_files, "lookup_path", raise_permission_error)
response = TestClient(app).get("/asset.txt")
assert response.status_code == 401
def raise_value_error(path: str):
raise ValueError
monkeypatch.setattr(static_files, "lookup_path", raise_value_error)
response = TestClient(app).get("/asset.txt")
assert response.status_code == 404
def raise_name_too_long(path: str):
raise OSError(errno.ENAMETOOLONG, "name too long")
monkeypatch.setattr(static_files, "lookup_path", raise_name_too_long)
response = TestClient(app).get("/asset.txt")
assert response.status_code == 404
def raise_os_error(path: str):
raise OSError(5, "other")
monkeypatch.setattr(static_files, "lookup_path", raise_os_error)
with pytest.raises(OSError):
TestClient(app).get("/asset.txt")
def test_frontend_route_group_helpers(tmp_path: Path):
dist = tmp_path / "dist"
write_file(dist / "index.html", "app")
app = FastAPI()
app.frontend("/", directory=dist)
route_group = app.router._frontend_routes
assert route_group is not None
match, child_scope = route_group.matches({"type": "websocket", "path": "/"})
assert match == Match.NONE
assert child_scope == {}
with pytest.raises(StarletteHTTPException) as exc_info:
anyio.run(
route_group.with_prefix("/app").handle,
{"type": "http", "path": "/missing", "method": "GET"},
None,
None,
)
assert exc_info.value.status_code == 404
with pytest.raises(NoMatchFound):
route_group.url_path_for("frontend")
with pytest.raises(NoMatchFound):
route_group.routes[0].url_path_for("frontend")
def test_included_low_priority_routes_cache_is_reused():
async def low_priority_endpoint(request: Request):
return PlainTextResponse("low")
router = APIRouter()
router._low_priority_routes.append(Route("/low", low_priority_endpoint))
router._mark_routes_changed()
app = FastAPI()
app.include_router(router, prefix="/prefix")
included_router = next(
route
for route in app.router.routes
if hasattr(route, "effective_low_priority_routes")
)
first = included_router.effective_low_priority_routes() # ty: ignore[call-non-callable]
second = included_router.effective_low_priority_routes() # ty: ignore[call-non-callable]
response = TestClient(app).get("/prefix/low")
assert first is second
assert response.status_code == 200
assert response.text == "low"
def test_low_priority_api_route_handles_with_context():
app = FastAPI()
async def endpoint(request: Request) -> Response:
return PlainTextResponse(request.scope["path_params"]["item_id"])
route = app.router.route_class("/low/{item_id}", endpoint=endpoint, methods=["GET"])
app.router._low_priority_routes.append(route)
app.router._mark_routes_changed()
response = TestClient(app).get("/low/abc")
assert response.status_code == 200
assert response.text == "abc"
def test_included_low_priority_api_route_handles_with_context():
router = APIRouter()
async def endpoint(request: Request) -> Response:
return PlainTextResponse(request.scope["path_params"]["item_id"])
route = router.route_class("/low/{item_id}", endpoint=endpoint, methods=["GET"])
router._low_priority_routes.append(route)
router._mark_routes_changed()
app = FastAPI()
app.include_router(router, prefix="/prefix")
response = TestClient(app).get("/prefix/low/abc")
assert response.status_code == 200
assert response.text == "abc"
def test_normal_route_partial_match_returns_before_frontend(tmp_path: Path):
class PartialRoute(BaseRoute):
def matches(self, scope):
return Match.PARTIAL, {}
async def handle(self, scope, receive, send):
response = PlainTextResponse("partial", status_code=405)
await response(scope, receive, send)
dist = tmp_path / "dist"
write_file(dist / "index.html", "frontend")
app = FastAPI()
app.router.routes.append(PartialRoute())
app.frontend("/", directory=dist)
response = TestClient(app).get("/anything")
assert response.status_code == 405
assert response.text == "partial"
def test_normal_route_partial_match_wins_before_frontend(tmp_path: Path):
dist = tmp_path / "dist"
write_file(dist / "api", "frontend")
app = FastAPI()
@app.get("/api")
def read_api():
return {"source": "api"}
app.frontend("/", directory=dist)
client = TestClient(app)
response = client.get("/api")
assert response.status_code == 200
assert response.json() == {"source": "api"}
response = client.post("/api")
assert response.status_code == 405
def test_basic_file_serving(tmp_path: Path):
dist = tmp_path / "dist"
write_file(dist / "assets" / "app.js", "console.log('ok')")
app = FastAPI()
app.frontend("/", directory=dist)
response = TestClient(app).get("/assets/app.js")
assert response.status_code == 200
assert response.text == "console.log('ok')"
assert "etag" in response.headers
assert "last-modified" in response.headers
def test_existing_api_route_wins_over_frontend(tmp_path: Path):
dist = tmp_path / "dist"
write_file(dist / "api" / "users", "frontend")
app = FastAPI()
@app.get("/api/users")
def read_users():
return {"source": "api"}
app.frontend("/", directory=dist)
response = TestClient(app).get("/api/users")
assert response.status_code == 200
assert response.json() == {"source": "api"}
def test_api_route_404_is_not_replaced_by_frontend_fallback(tmp_path: Path):
dist = tmp_path / "dist"
write_file(dist / "index.html", "frontend")
app = FastAPI()
@app.get("/api/users")
def read_users():
raise HTTPException(status_code=404, detail="api missing")
app.frontend("/", directory=dist, fallback="index.html")
response = TestClient(app).get("/api/users", headers={"accept": "text/html"})
assert response.status_code == 404
assert response.json() == {"detail": "api missing"}
def test_index_fallback_for_navigation_request(tmp_path: Path):
dist = tmp_path / "dist"
write_file(dist / "index.html", "app shell")
app = FastAPI()
app.frontend("/", directory=dist, fallback="index.html")
response = TestClient(app).get(
"/dashboard/settings", headers={"accept": "text/html"}
)
assert response.status_code == 200
assert response.text == "app shell"
def test_index_fallback_parses_accept_parameters(tmp_path: Path):
dist = tmp_path / "dist"
write_file(dist / "index.html", "app shell")
app = FastAPI()
app.frontend("/", directory=dist, fallback="index.html")
response = TestClient(app).get(
"/dashboard/settings", headers={"accept": "text/html; q=0.8"}
)
assert response.status_code == 200
assert response.text == "app shell"
def test_index_fallback_ignores_q_zero_accept(tmp_path: Path):
dist = tmp_path / "dist"
write_file(dist / "index.html", "app shell")
app = FastAPI()
app.frontend("/", directory=dist, fallback="index.html")
response = TestClient(app).get(
"/dashboard/settings", headers={"accept": "text/html; q=0.0"}
)
assert response.status_code == 404
def test_index_fallback_respects_explicit_html_rejection_with_wildcard(
tmp_path: Path,
):
dist = tmp_path / "dist"
write_file(dist / "index.html", "app shell")
app = FastAPI()
app.frontend("/", directory=dist, fallback="index.html")
response = TestClient(app).get(
"/dashboard/settings",
headers={"accept": "text/html; q=0, */*; q=1"},
)
assert response.status_code == 404
def test_index_fallback_respects_explicit_xhtml_rejection_with_wildcard(
tmp_path: Path,
):
dist = tmp_path / "dist"
write_file(dist / "index.html", "app shell")
app = FastAPI()
app.frontend("/", directory=dist, fallback="index.html")
response = TestClient(app).get(
"/dashboard/settings",
headers={"accept": "application/xhtml+xml; q=0, */*; q=1"},
)
assert response.status_code == 404
@pytest.mark.parametrize(
("path", "accept"),
[
("/assets/missing.js", "*/*"),
("/assets/missing.css", "text/css"),
("/assets/missing.png", "image/png"),
("/api/missing", "application/json"),
("/users/jane.doe", "text/html"),
],
)
def test_index_fallback_does_not_handle_asset_like_or_non_html_requests(
tmp_path: Path, path: str, accept: str
):
dist = tmp_path / "dist"
write_file(dist / "index.html", "app shell")
app = FastAPI()
app.frontend("/", directory=dist, fallback="index.html")
response = TestClient(app).get(path, headers={"accept": accept})
assert response.status_code == 404
assert response.text != "app shell"
def test_404_fallback_handles_missing_assets(tmp_path: Path):
dist = tmp_path / "dist"
write_file(dist / "404.html", "missing")
app = FastAPI()
app.frontend("/", directory=dist, fallback="404.html")
response = TestClient(app).get("/assets/missing.js")
assert response.status_code == 404
assert response.text == "missing"
def test_auto_fallback_prefers_404_over_index(tmp_path: Path):
dist = tmp_path / "dist"
write_file(dist / "index.html", "app shell")
write_file(dist / "404.html", "missing")
app = FastAPI()
app.frontend("/", directory=dist)
response = TestClient(app).get("/dashboard", headers={"accept": "text/html"})
assert response.status_code == 404
assert response.text == "missing"
def test_auto_fallback_uses_index_when_404_is_missing(tmp_path: Path):
dist = tmp_path / "dist"
write_file(dist / "index.html", "app shell")
app = FastAPI()
app.frontend("/", directory=dist)
response = TestClient(app).get("/dashboard", headers={"accept": "text/html"})
assert response.status_code == 200
assert response.text == "app shell"
def test_auto_fallback_returns_normal_404_without_fallback_files(tmp_path: Path):
dist = tmp_path / "dist"
dist.mkdir()
app = FastAPI()
app.frontend("/", directory=dist)
response = TestClient(app).get("/dashboard", headers={"accept": "text/html"})
assert response.status_code == 404
assert response.json() == {"detail": "Not Found"}
def test_no_fallback_returns_normal_404(tmp_path: Path):
dist = tmp_path / "dist"
write_file(dist / "index.html", "app shell")
app = FastAPI()
app.frontend("/", directory=dist, fallback=None)
response = TestClient(app).get("/dashboard", headers={"accept": "text/html"})
assert response.status_code == 404
assert response.json() == {"detail": "Not Found"}
def test_directory_index_and_redirect(tmp_path: Path):
dist = tmp_path / "dist"
write_file(dist / "about" / "index.html", "about")
app = FastAPI()
app.frontend("/", directory=dist)
client = TestClient(app)
redirect = client.get("/about", follow_redirects=False)
response = client.get("/about/")
assert redirect.status_code == 307
assert redirect.headers["location"] == "http://testserver/about/"
assert response.status_code == 200
assert response.text == "about"
def test_path_validation_and_trailing_slash_normalization(tmp_path: Path):
dist = tmp_path / "dist"
write_file(dist / "asset.txt", "ok")
app = FastAPI()
with pytest.raises(AssertionError):
app.frontend("", directory=dist)
with pytest.raises(AssertionError):
app.frontend("app", directory=dist)
app.frontend("/app/", directory=dist)
response = TestClient(app).get("/app/asset.txt")
assert response.status_code == 200
assert response.text == "ok"
def test_frontend_path_matching_uses_segment_boundaries(tmp_path: Path):
dist = tmp_path / "dist"
write_file(dist / "index.html", "app")
app = FastAPI()
app.frontend("/app", directory=dist, fallback="index.html")
response = TestClient(app).get("/application", headers={"accept": "text/html"})
assert response.status_code == 404
def test_multiple_frontends_use_longest_matching_prefix(tmp_path: Path):
site = tmp_path / "site"
admin = tmp_path / "admin"
write_file(site / "index.html", "site")
write_file(admin / "index.html", "admin")
app = FastAPI()
app.frontend("/", directory=site, fallback="index.html")
app.frontend("/admin", directory=admin, fallback="index.html")
response = TestClient(app).get("/admin/settings", headers={"accept": "text/html"})
assert response.status_code == 200
assert response.text == "admin"
def test_apirouter_frontend_uses_include_prefix(tmp_path: Path):
dist = tmp_path / "admin"
write_file(dist / "index.html", "admin")
router = APIRouter()
router.frontend("/", directory=dist, fallback="index.html")
app = FastAPI()
app.include_router(router, prefix="/admin")
response = TestClient(app).get("/admin/settings", headers={"accept": "text/html"})
assert response.status_code == 200
assert response.text == "admin"
def test_global_priority_across_included_routers(tmp_path: Path):
dist = tmp_path / "site"
write_file(dist / "index.html", "site")
site_router = APIRouter()
site_router.frontend("/", directory=dist, fallback="index.html")
api_router = APIRouter()
@api_router.get("/api/users")
def read_users():
return {"source": "api"}
app = FastAPI()
app.include_router(site_router)
app.include_router(api_router)
response = TestClient(app).get("/api/users", headers={"accept": "text/html"})
assert response.status_code == 200
assert response.json() == {"source": "api"}
def test_nested_apirouter_frontend_uses_all_include_prefixes(tmp_path: Path):
dist = tmp_path / "admin"
write_file(dist / "index.html", "admin")
child_router = APIRouter()
child_router.frontend("/", directory=dist, fallback="index.html")
parent_router = APIRouter()
parent_router.include_router(child_router, prefix="/child")
app = FastAPI()
app.include_router(parent_router, prefix="/parent")
response = TestClient(app).get(
"/parent/child/settings", headers={"accept": "text/html"}
)
assert response.status_code == 200
assert response.text == "admin"
def test_low_priority_cache_updates_after_route_added_to_included_router(
tmp_path: Path,
):
dist = tmp_path / "site"
write_file(dist / "index.html", "site")
router = APIRouter()
router.frontend("/", directory=dist, fallback="index.html")
app = FastAPI()
app.include_router(router, prefix="/app")
client = TestClient(app)
frontend_response = client.get("/app/dashboard", headers={"accept": "text/html"})
@router.get("/dashboard")
def read_dashboard():
return {"source": "api"}
api_response = client.get("/app/dashboard", headers={"accept": "text/html"})
assert frontend_response.status_code == 200
assert frontend_response.text == "site"
assert api_response.status_code == 200
assert api_response.json() == {"source": "api"}
def test_normal_route_slash_redirect_wins_before_frontend_redirect(tmp_path: Path):
dist = tmp_path / "site"
write_file(dist / "api" / "index.html", "frontend")
app = FastAPI()
@app.get("/api/")
def read_api():
return {"source": "api"}
app.frontend("/", directory=dist)
response = TestClient(app).get("/api", follow_redirects=False)
assert response.status_code == 307
assert response.headers["location"] == "http://testserver/api/"
followed = TestClient(app).get("/api/")
assert followed.status_code == 200
assert followed.json() == {"source": "api"}
def test_frontend_respects_root_path(tmp_path: Path):
dist = tmp_path / "dist"
write_file(dist / "assets" / "app.js", "console.log('ok')")
app = FastAPI()
app.frontend("/app", directory=dist)
response = TestClient(app, root_path="/proxy").get("/app/assets/app.js")
assert response.status_code == 200
assert response.text == "console.log('ok')"
def test_websocket_route_wins_over_frontend(tmp_path: Path):
dist = tmp_path / "dist"
write_file(dist / "ws", "frontend")
app = FastAPI()
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
await websocket.send_text("websocket")
await websocket.close()
app.frontend("/", directory=dist)
with TestClient(app).websocket_connect("/ws") as websocket:
data = websocket.receive_text()
assert data == "websocket"
def test_head_requests_work(tmp_path: Path):
dist = tmp_path / "dist"
write_file(dist / "asset.txt", "ok")
app = FastAPI()
app.frontend("/", directory=dist)
response = TestClient(app).head("/asset.txt")
assert response.status_code == 200
assert response.text == ""
assert response.headers["content-length"] == "2"
def test_unsupported_methods_return_405(tmp_path: Path):
dist = tmp_path / "dist"
write_file(dist / "asset.txt", "ok")
app = FastAPI()
app.frontend("/", directory=dist)
response = TestClient(app).post("/asset.txt")
assert response.status_code == 405
@pytest.mark.parametrize(
"path",
[
"/../secret.txt",
"/%2e%2e/secret.txt",
"/..%2fsecret.txt",
"/%5c..%5csecret.txt",
"/..%5csecret.txt",
],
)
def test_path_traversal_cannot_escape_directory(tmp_path: Path, path: str):
dist = tmp_path / "dist"
write_file(dist / "index.html", "app")
write_file(tmp_path / "secret.txt", "secret")
app = FastAPI()
app.frontend("/", directory=dist)
response = TestClient(app).get(path)
assert response.status_code == 404
assert response.text != "secret"
def test_symlink_outside_directory_is_not_served(tmp_path: Path):
dist = tmp_path / "dist"
dist.mkdir()
outside = tmp_path / "secret.txt"
outside.write_text("secret")
link = dist / "secret.txt"
try:
os.symlink(outside, link)
except (OSError, NotImplementedError): # pragma: no cover
pytest.skip("symlinks are not supported")
app = FastAPI()
app.frontend("/", directory=dist)
response = TestClient(app).get("/secret.txt")
assert response.status_code == 404
assert response.text != "secret"
def test_check_dir_true_fails_early_for_missing_directory(monkeypatch, tmp_path: Path):
app = FastAPI()
monkeypatch.chdir(tmp_path)
with pytest.raises(RuntimeError, match="does not exist") as exc_info:
app.frontend("/", directory="missing")
message = str(exc_info.value)
assert "'missing'" in message
assert str(tmp_path / "missing") in message
def test_check_dir_false_allows_missing_directory_and_fails_on_request(tmp_path: Path):
app = FastAPI()
app.frontend("/", directory=tmp_path / "missing", check_dir=False)
with pytest.raises(RuntimeError, match="does not exist"):
TestClient(app).get("/asset.txt")
def test_explicit_fallback_files_fail_clearly_when_missing(monkeypatch, tmp_path: Path):
dist = tmp_path / "dist"
dist.mkdir()
monkeypatch.chdir(tmp_path)
app = FastAPI()
with pytest.raises(RuntimeError, match="index.html") as exc_info:
app.frontend("/", directory="dist", fallback="index.html")
message = str(exc_info.value)
assert "directory 'dist'" in message
assert str(dist) in message
app = FastAPI()
app.frontend("/", directory="dist", fallback="404.html", check_dir=False)
with pytest.raises(RuntimeError, match="404.html") as exc_info:
TestClient(app).get("/missing.js")
message = str(exc_info.value)
assert "directory 'dist'" in message
assert str(dist) in message
def test_frontend_routes_are_not_in_openapi(tmp_path: Path):
dist = tmp_path / "dist"
write_file(dist / "index.html", "app")
app = FastAPI()
@app.get("/api")
def read_api():
return {"ok": True}
app.frontend("/", directory=dist, fallback="index.html")
schema = TestClient(app).get("/openapi.json").json()
assert set(schema["paths"]) == {"/api"}
response = TestClient(app).get("/api")
assert response.status_code == 200
assert response.json() == {"ok": True}
@pytest.mark.parametrize(
("example", "files", "path", "status_code", "body"),
[
(
"tutorial001_py310.py",
{"asset.txt": "asset"},
"/asset.txt",
200,
"asset",
),
(
"tutorial002_py310.py",
{"index.html": "index"},
"/dashboard",
200,
"index",
),
(
"tutorial003_py310.py",
{"404.html": "missing"},
"/missing",
404,
"missing",
),
(
"tutorial004_py310.py",
{"index.html": "index"},
"/app/dashboard",
200,
"index",
),
(
"tutorial005_py310.py",
{"index.html": "index"},
"/dashboard",
404,
'{"detail":"Not Found"}',
),
(
"tutorial006_py310.py",
{"asset.txt": "asset"},
"/asset.txt",
200,
"asset",
),
],
)
def test_docs_frontend_examples(
tmp_path: Path,
monkeypatch,
example: str,
files: dict[str, str],
path: str,
status_code: int,
body: str,
):
dist = tmp_path / "dist"
for file, content in files.items():
write_file(dist / file, content)
monkeypatch.chdir(tmp_path)
namespace = runpy.run_path(
str(Path(__file__).parents[1] / "docs_src" / "frontend" / example)
)
app = namespace["app"]
assert isinstance(app, FastAPI)
response = TestClient(app).get(path, headers={"accept": "text/html"})
assert response.status_code == status_code
assert response.text == body
def test_low_priority_routes_can_store_non_frontend_routes():
async def low_priority_endpoint(request):
return PlainTextResponse("low")
app = FastAPI()
app.router._low_priority_routes.append(Route("/low", low_priority_endpoint))
app.router._mark_routes_changed()
response = TestClient(app).get("/low")
assert response.status_code == 200
assert response.text == "low"
def test_included_low_priority_routes_can_store_non_frontend_routes():
async def low_priority_endpoint(request):
return PlainTextResponse("low")
router = APIRouter()
router._low_priority_routes.append(Route("/low", low_priority_endpoint))
router._mark_routes_changed()
app = FastAPI()
app.include_router(router, prefix="/prefix")
response = TestClient(app).get("/prefix/low")
assert response.status_code == 200
assert response.text == "low"